shill: cellular: Move cellular code to its own sub-directory.
This CL is part of a series of CLs, which aim to reorganzie shill's flat
source code structure into a more modular form by moving technology
specific code into their own sub-directories.
BUG=chromium:433419
TEST=`USE='cellular' FEATURES=test emerge-$BOARD shill`
TEST=`USE='cellular clang asan' FEATURES=test emerge-$BOARD shill`
Change-Id: I783e85d8c606426ce2ded093588c1243fd0eef97
Reviewed-on: https://chromium-review.googlesource.com/229799
Reviewed-by: Thieu Le <thieule@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Ben Chan <benchan@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
diff --git a/cellular/active_passive_out_of_credits_detector.cc b/cellular/active_passive_out_of_credits_detector.cc
new file mode 100644
index 0000000..4a92efc
--- /dev/null
+++ b/cellular/active_passive_out_of_credits_detector.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2013 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/active_passive_out_of_credits_detector.h"
+
+#include <string>
+
+#include "shill/cellular/cellular_service.h"
+#include "shill/connection.h"
+#include "shill/connection_health_checker.h"
+#include "shill/logging.h"
+#include "shill/manager.h"
+#include "shill/traffic_monitor.h"
+
+using std::string;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(ActivePassiveOutOfCreditsDetector *a) {
+ return a->GetServiceRpcIdentifier();
+}
+}
+
+// static
+const int64_t
+ ActivePassiveOutOfCreditsDetector::kOutOfCreditsConnectionDropSeconds = 15;
+const int
+ ActivePassiveOutOfCreditsDetector::kOutOfCreditsMaxConnectAttempts = 3;
+const int64_t
+ ActivePassiveOutOfCreditsDetector::kOutOfCreditsResumeIgnoreSeconds = 5;
+
+ActivePassiveOutOfCreditsDetector::ActivePassiveOutOfCreditsDetector(
+ EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service)
+ : OutOfCreditsDetector(dispatcher, manager, metrics, service),
+ weak_ptr_factory_(this),
+ traffic_monitor_(
+ new TrafficMonitor(service->cellular(), dispatcher)),
+ service_rpc_identifier_(service->GetRpcIdentifier()) {
+ ResetDetector();
+ traffic_monitor_->set_network_problem_detected_callback(
+ Bind(&ActivePassiveOutOfCreditsDetector::OnNoNetworkRouting,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+ActivePassiveOutOfCreditsDetector::~ActivePassiveOutOfCreditsDetector() {
+ StopTrafficMonitor();
+}
+
+void ActivePassiveOutOfCreditsDetector::ResetDetector() {
+ SLOG(this, 2) << "Reset out-of-credits detection";
+ out_of_credits_detection_in_progress_ = false;
+ num_connect_attempts_ = 0;
+}
+
+bool ActivePassiveOutOfCreditsDetector::IsDetecting() const {
+ return out_of_credits_detection_in_progress_;
+}
+
+void ActivePassiveOutOfCreditsDetector::NotifyServiceStateChanged(
+ Service::ConnectState old_state, Service::ConnectState new_state) {
+ SLOG(this, 2) << __func__ << ": " << old_state << " -> " << new_state;
+ switch (new_state) {
+ case Service::kStateUnknown:
+ case Service::kStateIdle:
+ case Service::kStateFailure:
+ StopTrafficMonitor();
+ health_checker_.reset();
+ break;
+ case Service::kStateAssociating:
+ if (num_connect_attempts_ == 0)
+ ReportOutOfCredits(false);
+ if (old_state != Service::kStateAssociating) {
+ connect_start_time_ = base::Time::Now();
+ num_connect_attempts_++;
+ SLOG(this, 2) << __func__
+ << ": num_connect_attempts="
+ << num_connect_attempts_;
+ }
+ break;
+ case Service::kStateConnected:
+ StartTrafficMonitor();
+ SetupConnectionHealthChecker();
+ break;
+ case Service::kStatePortal:
+ SLOG(this, 2) << "Portal detection failed. Launching active probe "
+ << "for out-of-credit detection.";
+ RequestConnectionHealthCheck();
+ break;
+ case Service::kStateConfiguring:
+ case Service::kStateOnline:
+ break;
+ }
+ DetectConnectDisconnectLoop(old_state, new_state);
+}
+
+bool ActivePassiveOutOfCreditsDetector::StartTrafficMonitor() {
+ SLOG(this, 2) << __func__;
+ SLOG(this, 2) << "Service " << service()->friendly_name()
+ << ": Traffic Monitor starting.";
+ traffic_monitor_->Start();
+ return true;
+}
+
+void ActivePassiveOutOfCreditsDetector::StopTrafficMonitor() {
+ SLOG(this, 2) << __func__;
+ SLOG(this, 2) << "Service " << service()->friendly_name()
+ << ": Traffic Monitor stopping.";
+ traffic_monitor_->Stop();
+}
+
+void ActivePassiveOutOfCreditsDetector::OnNoNetworkRouting(int reason) {
+ SLOG(this, 2) << "Service " << service()->friendly_name()
+ << ": Traffic Monitor detected network congestion.";
+ SLOG(this, 2) << "Requesting active probe for out-of-credit detection.";
+ RequestConnectionHealthCheck();
+}
+
+void ActivePassiveOutOfCreditsDetector::SetupConnectionHealthChecker() {
+ DCHECK(service()->connection());
+ // TODO(thieule): Consider moving health_checker_remote_ips() out of manager
+ // (crbug.com/304974).
+ if (!health_checker_.get()) {
+ health_checker_.reset(
+ new ConnectionHealthChecker(
+ service()->connection(),
+ dispatcher(),
+ manager()->health_checker_remote_ips(),
+ Bind(&ActivePassiveOutOfCreditsDetector::
+ OnConnectionHealthCheckerResult,
+ weak_ptr_factory_.GetWeakPtr())));
+ } else {
+ health_checker_->SetConnection(service()->connection());
+ }
+ // Add URL in either case because a connection reset could have dropped past
+ // DNS queries.
+ health_checker_->AddRemoteURL(manager()->GetPortalCheckURL());
+}
+
+void ActivePassiveOutOfCreditsDetector::RequestConnectionHealthCheck() {
+ if (!health_checker_.get()) {
+ SLOG(this, 2) << "No health checker exists, cannot request "
+ << "health check.";
+ return;
+ }
+ if (health_checker_->health_check_in_progress()) {
+ SLOG(this, 2) << "Health check already in progress.";
+ return;
+ }
+ health_checker_->Start();
+}
+
+void ActivePassiveOutOfCreditsDetector::OnConnectionHealthCheckerResult(
+ ConnectionHealthChecker::Result result) {
+ SLOG(this, 2) << __func__ << "(Result = "
+ << ConnectionHealthChecker::ResultToString(result) << ")";
+
+ if (result == ConnectionHealthChecker::kResultCongestedTxQueue) {
+ LOG(WARNING) << "Active probe determined possible out-of-credits "
+ << "scenario.";
+ if (service()) {
+ Metrics::CellularOutOfCreditsReason reason =
+ (result == ConnectionHealthChecker::kResultCongestedTxQueue) ?
+ Metrics::kCellularOutOfCreditsReasonTxCongested :
+ Metrics::kCellularOutOfCreditsReasonElongatedTimeWait;
+ metrics()->NotifyCellularOutOfCredits(reason);
+
+ ReportOutOfCredits(true);
+ SLOG(this, 2) << "Disconnecting due to out-of-credit scenario.";
+ Error error;
+ service()->Disconnect(&error, "out-of-credits");
+ }
+ }
+}
+
+void ActivePassiveOutOfCreditsDetector::DetectConnectDisconnectLoop(
+ Service::ConnectState curr_state, Service::ConnectState new_state) {
+ // WORKAROUND:
+ // Some modems on Verizon network do not properly redirect when a SIM
+ // runs out of credits. This workaround is used to detect an out-of-credits
+ // condition by retrying a connect request if it was dropped within
+ // kOutOfCreditsConnectionDropSeconds. If the number of retries exceeds
+ // kOutOfCreditsMaxConnectAttempts, then the SIM is considered
+ // out-of-credits and the cellular service kOutOfCreditsProperty is set.
+ // This will signal Chrome to display the appropriate UX and also suppress
+ // auto-connect until the next time the user manually connects.
+ //
+ // TODO(thieule): Remove this workaround (crosbug.com/p/18169).
+ if (out_of_credits()) {
+ SLOG(this, 2) << __func__
+ << ": Already out-of-credits, skipping check";
+ return;
+ }
+ base::TimeDelta
+ time_since_resume = base::Time::Now() - service()->resume_start_time();
+ if (time_since_resume.InSeconds() < kOutOfCreditsResumeIgnoreSeconds) {
+ // On platforms that power down the modem during suspend, make sure that
+ // we do not display a false out-of-credits warning to the user
+ // due to the sequence below by skipping out-of-credits detection
+ // immediately after a resume.
+ // 1. User suspends Chromebook.
+ // 2. Hardware turns off power to modem.
+ // 3. User resumes Chromebook.
+ // 4. Hardware restores power to modem.
+ // 5. ModemManager still has instance of old modem.
+ // ModemManager does not delete this instance until udev fires a
+ // device removed event. ModemManager does not detect new modem
+ // until udev fires a new device event.
+ // 6. Shill performs auto-connect against the old modem.
+ // Make sure at this step that we do not display a false
+ // out-of-credits warning.
+ // 7. Udev fires device removed event.
+ // 8. Udev fires new device event.
+ SLOG(this, 2) <<
+ "Skipping out-of-credits detection, too soon since resume.";
+ ResetDetector();
+ return;
+ }
+ base::TimeDelta
+ time_since_connect = base::Time::Now() - connect_start_time_;
+ if (time_since_connect.InSeconds() > kOutOfCreditsConnectionDropSeconds) {
+ ResetDetector();
+ return;
+ }
+ // Verizon can drop the connection in two ways:
+ // - Denies the connect request
+ // - Allows connect request but disconnects later
+ bool connection_dropped =
+ (Service::IsConnectedState(curr_state) ||
+ Service::IsConnectingState(curr_state)) &&
+ (new_state == Service::kStateFailure ||
+ new_state == Service::kStateIdle);
+ if (!connection_dropped)
+ return;
+ if (service()->explicitly_disconnected())
+ return;
+ if (service()->roaming_state() == kRoamingStateRoaming &&
+ !service()->cellular()->allow_roaming_property())
+ return;
+ if (time_since_connect.InSeconds() <= kOutOfCreditsConnectionDropSeconds) {
+ if (num_connect_attempts_ < kOutOfCreditsMaxConnectAttempts) {
+ SLOG(this, 2) << "Out-Of-Credits detection: Reconnecting "
+ << "(retry #" << num_connect_attempts_ << ")";
+ // Prevent autoconnect logic from kicking in while we perform the
+ // out-of-credits detection.
+ out_of_credits_detection_in_progress_ = true;
+ dispatcher()->PostTask(
+ Bind(&ActivePassiveOutOfCreditsDetector::OutOfCreditsReconnect,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ LOG(INFO) << "Active/Passive Out-Of-Credits detection: "
+ << "Marking service as out-of-credits";
+ metrics()->NotifyCellularOutOfCredits(
+ Metrics::kCellularOutOfCreditsReasonConnectDisconnectLoop);
+ ReportOutOfCredits(true);
+ ResetDetector();
+ }
+ }
+}
+
+void ActivePassiveOutOfCreditsDetector::OutOfCreditsReconnect() {
+ Error error;
+ service()->Connect(&error, __func__);
+}
+
+void ActivePassiveOutOfCreditsDetector::set_traffic_monitor(
+ TrafficMonitor *traffic_monitor) {
+ traffic_monitor_.reset(traffic_monitor);
+}
+
+void ActivePassiveOutOfCreditsDetector::set_connection_health_checker(
+ ConnectionHealthChecker *health_checker) {
+ health_checker_.reset(health_checker);
+}
+
+} // namespace shill
diff --git a/cellular/active_passive_out_of_credits_detector.h b/cellular/active_passive_out_of_credits_detector.h
new file mode 100644
index 0000000..53b74c3
--- /dev/null
+++ b/cellular/active_passive_out_of_credits_detector.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_CELLULAR_ACTIVE_PASSIVE_OUT_OF_CREDITS_DETECTOR_H_
+#define SHILL_CELLULAR_ACTIVE_PASSIVE_OUT_OF_CREDITS_DETECTOR_H_
+
+#include <memory>
+#include <string>
+
+#include <base/time/time.h>
+
+#include "shill/cellular/out_of_credits_detector.h"
+#include "shill/connection_health_checker.h"
+
+namespace shill {
+
+// Detects out-of-credits condition by monitoring for the following scenarios:
+// - Passively watch for network congestion and launch active probes to
+// determine if the network has stopped routing traffic.
+// - Watch for connect/disconnect loop.
+class ActivePassiveOutOfCreditsDetector : public OutOfCreditsDetector {
+ public:
+ ActivePassiveOutOfCreditsDetector(EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service);
+ ~ActivePassiveOutOfCreditsDetector() override;
+
+ void ResetDetector() override;
+ bool IsDetecting() const override;
+ void NotifyServiceStateChanged(
+ Service::ConnectState old_state,
+ Service::ConnectState new_state) override;
+ void NotifySubscriptionStateChanged(uint32_t subscription_state) override {}
+
+ const TrafficMonitor *traffic_monitor() const {
+ return traffic_monitor_.get();
+ }
+
+ const std::string &GetServiceRpcIdentifier() const {
+ return service_rpc_identifier_;
+ }
+
+ private:
+ friend class ActivePassiveOutOfCreditsDetectorTest;
+ FRIEND_TEST(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionIntermittentNetwork);
+ FRIEND_TEST(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionNotSkippedAfterSlowResume);
+ FRIEND_TEST(ActivePassiveOutOfCreditsDetectorTest,
+ OnConnectionHealthCheckerResult);
+ FRIEND_TEST(ActivePassiveOutOfCreditsDetectorTest, OnNoNetworkRouting);
+ FRIEND_TEST(ActivePassiveOutOfCreditsDetectorTest, StopTrafficMonitor);
+
+ static const int64_t kOutOfCreditsConnectionDropSeconds;
+ static const int kOutOfCreditsMaxConnectAttempts;
+ static const int64_t kOutOfCreditsResumeIgnoreSeconds;
+
+ // Initiates traffic monitoring.
+ bool StartTrafficMonitor();
+
+ // Stops traffic monitoring.
+ void StopTrafficMonitor();
+
+ // Responds to a TrafficMonitor no-network-routing failure.
+ void OnNoNetworkRouting(int reason);
+
+ // Initializes and configures the connection health checker.
+ void SetupConnectionHealthChecker();
+
+ // Checks the network connectivity status by creating a TCP connection, and
+ // optionally sending a small amout of data.
+ void RequestConnectionHealthCheck();
+
+ // Responds to the result from connection health checker in a device specific
+ // manner.
+ void OnConnectionHealthCheckerResult(ConnectionHealthChecker::Result result);
+
+ // Performs out-of-credits detection by checking to see if we're stuck in a
+ // connect/disconnect loop.
+ void DetectConnectDisconnectLoop(Service::ConnectState curr_state,
+ Service::ConnectState new_state);
+ // Reconnects to the cellular service in the context of out-of-credits
+ // detection.
+ void OutOfCreditsReconnect();
+
+ // Ownership of |traffic_monitor| is taken.
+ void set_traffic_monitor(TrafficMonitor *traffic_monitor);
+
+ // Ownership of |healther_checker| is taken.
+ void set_connection_health_checker(ConnectionHealthChecker *health_checker);
+
+ base::WeakPtrFactory<ActivePassiveOutOfCreditsDetector> weak_ptr_factory_;
+
+ // Passively monitors network traffic for network failures.
+ std::unique_ptr<TrafficMonitor> traffic_monitor_;
+ // Determine network health through active probes.
+ std::unique_ptr<ConnectionHealthChecker> health_checker_;
+
+ // The following members are used by the connect/disconnect loop detection.
+ // Time when the last connect request started.
+ base::Time connect_start_time_;
+ // Number of connect attempts.
+ int num_connect_attempts_;
+ // Flag indicating whether out-of-credits detection is in progress.
+ bool out_of_credits_detection_in_progress_;
+
+ // String to hold service identifier for scoped logging.
+ std::string service_rpc_identifier_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActivePassiveOutOfCreditsDetector);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_ACTIVE_PASSIVE_OUT_OF_CREDITS_DETECTOR_H_
diff --git a/cellular/active_passive_out_of_credits_detector_unittest.cc b/cellular/active_passive_out_of_credits_detector_unittest.cc
new file mode 100644
index 0000000..1f95b14
--- /dev/null
+++ b/cellular/active_passive_out_of_credits_detector_unittest.cc
@@ -0,0 +1,320 @@
+// Copyright (c) 2013 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/active_passive_out_of_credits_detector.h"
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "shill/cellular/mock_cellular.h"
+#include "shill/cellular/mock_cellular_service.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_connection.h"
+#include "shill/mock_connection_health_checker.h"
+#include "shill/mock_device_info.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_proxy_factory.h"
+#include "shill/mock_traffic_monitor.h"
+
+using base::Bind;
+using base::Unretained;
+using std::string;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::ReturnPointee;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+namespace shill {
+
+class ActivePassiveOutOfCreditsDetectorTest : public testing::Test {
+ public:
+ ActivePassiveOutOfCreditsDetectorTest()
+ : modem_info_(nullptr, &dispatcher_, &metrics_, &manager_, nullptr),
+ device_info_(modem_info_.control_interface(), modem_info_.dispatcher(),
+ modem_info_.metrics(), modem_info_.manager()),
+ manager_(modem_info_.control_interface(), modem_info_.dispatcher(),
+ modem_info_.metrics(), modem_info_.glib()),
+ metrics_(modem_info_.dispatcher()),
+ cellular_(new NiceMock<MockCellular>(&modem_info_,
+ "usb0",
+ kAddress,
+ 3,
+ Cellular::kTypeCDMA,
+ "",
+ "",
+ "",
+ ProxyFactory::GetInstance())),
+ service_(new NiceMock<MockCellularService>(&modem_info_, cellular_)),
+ connection_(new NiceMock<MockConnection>(&device_info_)),
+ out_of_credits_detector_(
+ new ActivePassiveOutOfCreditsDetector(
+ modem_info_.dispatcher(), modem_info_.manager(),
+ modem_info_.metrics(), service_)) {}
+
+ virtual void SetUp() {
+ service_->connection_ = connection_;
+ cellular_->service_ = service_;
+ service_->SetRoamingState(kRoamingStateHome);
+ ON_CALL(*connection_, interface_name())
+ .WillByDefault(ReturnRef(interface_name_));
+ ON_CALL(*connection_, dns_servers())
+ .WillByDefault(ReturnRef(dns_servers_));
+ ON_CALL(manager_, GetPortalCheckURL())
+ .WillByDefault(ReturnRef(portal_check_url_));
+ ON_CALL(*service_, explicitly_disconnected()).WillByDefault(Return(false));
+ ON_CALL(*service_, resume_start_time())
+ .WillByDefault(ReturnRef(resume_start_time_));
+ }
+
+ virtual void TearDown() {
+ cellular_->service_ = nullptr; // Break circular reference.
+ }
+
+ void OnConnectionHealthCheckerResult(
+ ConnectionHealthChecker::Result result) {}
+
+ protected:
+ static const char kAddress[];
+
+ void SetMockServiceState(Service::ConnectState old_state,
+ Service::ConnectState new_state) {
+ out_of_credits_detector_->NotifyServiceStateChanged(old_state, new_state);
+ }
+
+ void SetTrafficMonitor(TrafficMonitor *traffic_monitor) {
+ out_of_credits_detector_->set_traffic_monitor(traffic_monitor);
+ }
+
+ void SetConnectionHealthChecker(ConnectionHealthChecker *health_checker) {
+ out_of_credits_detector_->set_connection_health_checker(health_checker);
+ }
+
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ NiceMock<MockDeviceInfo> device_info_;
+ NiceMock<MockManager> manager_;
+ NiceMock<MockMetrics> metrics_;
+ scoped_refptr<NiceMock<MockCellular>> cellular_;
+ scoped_refptr<NiceMock<MockCellularService>> service_;
+ scoped_refptr<NiceMock<MockConnection>> connection_;
+ string interface_name_;
+ vector<string> dns_servers_;
+ string portal_check_url_;
+ base::Time resume_start_time_;
+ std::unique_ptr<ActivePassiveOutOfCreditsDetector> out_of_credits_detector_;
+};
+
+const char ActivePassiveOutOfCreditsDetectorTest::kAddress[] = "000102030405";
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopOutOfCreditsDetected) {
+ EXPECT_CALL(*service_, Connect(_, _)).Times(2);
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ SetMockServiceState(Service::kStateConnected, Service::kStateFailure);
+ EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
+ dispatcher_.DispatchPendingEvents();
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
+ SetMockServiceState(Service::kStateConfiguring, Service::kStateIdle);
+ EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
+ dispatcher_.DispatchPendingEvents();
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
+ EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
+ EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionNotSkippedAfterSlowResume) {
+ resume_start_time_ =
+ base::Time::Now() -
+ base::TimeDelta::FromSeconds(
+ ActivePassiveOutOfCreditsDetector::kOutOfCreditsResumeIgnoreSeconds + 1);
+ EXPECT_CALL(*service_, Connect(_, _)).Times(2);
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateFailure);
+ EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
+ dispatcher_.DispatchPendingEvents();
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
+ SetMockServiceState(Service::kStateConfiguring, Service::kStateIdle);
+ EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
+ dispatcher_.DispatchPendingEvents();
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
+ EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
+ EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionSkippedAfterResume) {
+ resume_start_time_ = base::Time::Now();
+ ON_CALL(*service_, resume_start_time())
+ .WillByDefault(ReturnRef(resume_start_time_));
+ EXPECT_CALL(*service_, Connect(_, _)).Times(0);
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
+ // There should not be any pending connect requests but dispatch pending
+ // events anyway to be sure.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionSkippedAlreadyOutOfCredits) {
+ EXPECT_CALL(*service_, Connect(_, _)).Times(0);
+ out_of_credits_detector_->ReportOutOfCredits(true);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
+ EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
+ // There should not be any pending connect requests but dispatch pending
+ // events anyway to be sure.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionSkippedExplicitDisconnect) {
+ EXPECT_CALL(*service_, Connect(_, _)).Times(0);
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ EXPECT_CALL(*service_, explicitly_disconnected()).WillOnce(Return(true));
+ SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
+ // There should not be any pending connect requests but dispatch pending
+ // events anyway to be sure.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionConnectionNotDropped) {
+ EXPECT_CALL(*service_, Connect(_, _)).Times(0);
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
+ SetMockServiceState(Service::kStateConfiguring, Service::kStateConnected);
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
+ // There should not be any pending connect requests but dispatch pending
+ // events anyway to be sure.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionIntermittentNetwork) {
+ EXPECT_CALL(*service_, Connect(_, _)).Times(0);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ out_of_credits_detector_->connect_start_time_ =
+ base::Time::Now() -
+ base::TimeDelta::FromSeconds(
+ ActivePassiveOutOfCreditsDetector::
+ kOutOfCreditsConnectionDropSeconds + 1);
+ SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
+ // There should not be any pending connect requests but dispatch pending
+ // events anyway to be sure.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest, StartTrafficMonitor) {
+ MockTrafficMonitor *traffic_monitor = new StrictMock<MockTrafficMonitor>();
+ SetTrafficMonitor(traffic_monitor); // Passes ownership.
+
+ // Traffic monitor should only start when the service is connected.
+ EXPECT_CALL(*traffic_monitor, Start()).Times(1);
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ Mock::VerifyAndClearExpectations(traffic_monitor);
+
+ // Traffic monitor should not start for other state transitions.
+ EXPECT_CALL(*traffic_monitor, Start()).Times(0);
+ EXPECT_CALL(*traffic_monitor, Stop()).Times(AnyNumber());
+ SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
+ SetMockServiceState(Service::kStateIdle, Service::kStateConfiguring);
+ SetMockServiceState(Service::kStateConfiguring, Service::kStateFailure);
+ SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
+ SetMockServiceState(Service::kStateConfiguring, Service::kStatePortal);
+ SetMockServiceState(Service::kStatePortal, Service::kStateOnline);
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest, StopTrafficMonitor) {
+ // Traffic monitor should stop when the service is disconnected.
+ MockTrafficMonitor *traffic_monitor = new StrictMock<MockTrafficMonitor>();
+ SetTrafficMonitor(traffic_monitor); // Passes ownership.
+ EXPECT_CALL(*traffic_monitor, Start());
+ EXPECT_CALL(*traffic_monitor, Stop());
+ SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
+ SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
+ Mock::VerifyAndClearExpectations(traffic_monitor);
+
+ EXPECT_CALL(*traffic_monitor, Start());
+ EXPECT_CALL(*traffic_monitor, Stop());
+ SetMockServiceState(Service::kStateIdle, Service::kStateConnected);
+ SetMockServiceState(Service::kStateConnected, Service::kStateFailure);
+ Mock::VerifyAndClearExpectations(traffic_monitor);
+
+ // Need an additional call to Stop() because |traffic_monitor| destructor
+ // will call stop.
+ EXPECT_CALL(*traffic_monitor, Stop());
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest, OnNoNetworkRouting) {
+ // Make sure the connection health checker starts when there is no network
+ // routing.
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ MockConnectionHealthChecker *health_checker =
+ new MockConnectionHealthChecker(
+ service_->connection(),
+ modem_info_.dispatcher(),
+ manager_.health_checker_remote_ips(),
+ Bind(&ActivePassiveOutOfCreditsDetectorTest::
+ OnConnectionHealthCheckerResult,
+ Unretained(this)));
+ SetConnectionHealthChecker(health_checker); // Passes ownership.
+ EXPECT_CALL(*health_checker, Start());
+ out_of_credits_detector_->OnNoNetworkRouting(0);
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ Mock::VerifyAndClearExpectations(health_checker);
+
+ // Make sure connection health checker does not start again if there is a
+ // health check in progress.
+ EXPECT_CALL(*health_checker, health_check_in_progress())
+ .WillOnce(Return(true));
+ EXPECT_CALL(*health_checker, Start()).Times(0);
+ out_of_credits_detector_->OnNoNetworkRouting(0);
+}
+
+TEST_F(ActivePassiveOutOfCreditsDetectorTest,
+ OnConnectionHealthCheckerResult) {
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ EXPECT_CALL(*service_, Disconnect(_, _)).Times(0);
+ out_of_credits_detector_->OnConnectionHealthCheckerResult(
+ ConnectionHealthChecker::kResultUnknown);
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ out_of_credits_detector_->OnConnectionHealthCheckerResult(
+ ConnectionHealthChecker::kResultConnectionFailure);
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+ Mock::VerifyAndClearExpectations(service_);
+
+ EXPECT_CALL(*service_, Disconnect(_,
+ ::testing::StrEq("out-of-credits"))).
+ Times(1);
+ out_of_credits_detector_->OnConnectionHealthCheckerResult(
+ ConnectionHealthChecker::kResultCongestedTxQueue);
+ EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
+}
+
+} // namespace shill
diff --git a/cellular/cellular.cc b/cellular/cellular.cc
new file mode 100644
index 0000000..bf78bc5
--- /dev/null
+++ b/cellular/cellular.cc
@@ -0,0 +1,1604 @@
+// Copyright (c) 2012 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/cellular.h"
+
+#include <netinet/in.h>
+#include <linux/if.h> // NOLINT - Needs definitions from netinet/in.h
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/files/file_path.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/adaptor_interfaces.h"
+#include "shill/cellular/cellular_bearer.h"
+#include "shill/cellular/cellular_capability_cdma.h"
+#include "shill/cellular/cellular_capability_gsm.h"
+#include "shill/cellular/cellular_capability_universal.h"
+#include "shill/cellular/cellular_capability_universal_cdma.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/mobile_operator_info.h"
+#include "shill/control_interface.h"
+#include "shill/device.h"
+#include "shill/device_info.h"
+#include "shill/error.h"
+#include "shill/event_dispatcher.h"
+#include "shill/external_task.h"
+#include "shill/logging.h"
+#include "shill/manager.h"
+#include "shill/net/rtnl_handler.h"
+#include "shill/ppp_device.h"
+#include "shill/ppp_device_factory.h"
+#include "shill/profile.h"
+#include "shill/property_accessor.h"
+#include "shill/proxy_factory.h"
+#include "shill/store_interface.h"
+#include "shill/technology.h"
+
+using base::Bind;
+using base::Closure;
+using base::FilePath;
+using base::StringPrintf;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(Cellular *c) { return c->GetRpcIdentifier(); }
+}
+
+// static
+const char Cellular::kAllowRoaming[] = "AllowRoaming";
+const int64_t Cellular::kDefaultScanningTimeoutMilliseconds = 60000;
+const char Cellular::kGenericServiceNamePrefix[] = "MobileNetwork";
+unsigned int Cellular::friendly_service_name_id_ = 1;
+
+Cellular::Cellular(ModemInfo *modem_info,
+ const string &link_name,
+ const string &address,
+ int interface_index,
+ Type type,
+ const string &owner,
+ const string &service,
+ const string &path,
+ ProxyFactory *proxy_factory)
+ : Device(modem_info->control_interface(),
+ modem_info->dispatcher(),
+ modem_info->metrics(),
+ modem_info->manager(),
+ link_name,
+ address,
+ interface_index,
+ Technology::kCellular),
+ weak_ptr_factory_(this),
+ state_(kStateDisabled),
+ modem_state_(kModemStateUnknown),
+ home_provider_info_(
+ new MobileOperatorInfo(modem_info->dispatcher(), "HomeProvider")),
+ serving_operator_info_(
+ new MobileOperatorInfo(modem_info->dispatcher(), "ServingOperator")),
+ mobile_operator_info_observer_(
+ new Cellular::MobileOperatorInfoObserver(this)),
+ dbus_owner_(owner),
+ dbus_service_(service),
+ dbus_path_(path),
+ scanning_supported_(false),
+ scanning_(false),
+ provider_requires_roaming_(false),
+ scan_interval_(0),
+ sim_present_(false),
+ prl_version_(0),
+ modem_info_(modem_info),
+ type_(type),
+ proxy_factory_(proxy_factory),
+ ppp_device_factory_(PPPDeviceFactory::GetInstance()),
+ allow_roaming_(false),
+ proposed_scan_in_progress_(false),
+ explicit_disconnect_(false),
+ is_ppp_authenticating_(false),
+ scanning_timeout_milliseconds_(kDefaultScanningTimeoutMilliseconds) {
+ RegisterProperties();
+ InitCapability(type);
+
+ // TODO(pprabhu) Split MobileOperatorInfo into a context that stores the
+ // costly database, and lighter objects that |Cellular| can own.
+ // crbug.com/363874
+ home_provider_info_->Init();
+ serving_operator_info_->Init();
+ home_provider_info()->AddObserver(mobile_operator_info_observer_.get());
+ serving_operator_info()->AddObserver(mobile_operator_info_observer_.get());
+
+ SLOG(this, 2) << "Cellular device " << this->link_name()
+ << " initialized.";
+}
+
+Cellular::~Cellular() {
+ // 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.
+ manager()->RemoveTerminationAction(FriendlyName());
+
+ home_provider_info()->RemoveObserver(mobile_operator_info_observer_.get());
+ serving_operator_info()->RemoveObserver(
+ mobile_operator_info_observer_.get());
+ // Explicitly delete the observer to ensure that it is destroyed before the
+ // handle to |capability_| that it holds.
+ mobile_operator_info_observer_.reset();
+}
+
+bool Cellular::Load(StoreInterface *storage) {
+ const string id = GetStorageIdentifier();
+ if (!storage->ContainsGroup(id)) {
+ LOG(WARNING) << "Device is not available in the persistent store: " << id;
+ return false;
+ }
+ storage->GetBool(id, kAllowRoaming, &allow_roaming_);
+ return Device::Load(storage);
+}
+
+bool Cellular::Save(StoreInterface *storage) {
+ const string id = GetStorageIdentifier();
+ storage->SetBool(id, kAllowRoaming, allow_roaming_);
+ return Device::Save(storage);
+}
+
+// static
+string Cellular::GetStateString(State state) {
+ switch (state) {
+ case kStateDisabled:
+ return "CellularStateDisabled";
+ case kStateEnabled:
+ return "CellularStateEnabled";
+ case kStateRegistered:
+ return "CellularStateRegistered";
+ case kStateConnected:
+ return "CellularStateConnected";
+ case kStateLinked:
+ return "CellularStateLinked";
+ default:
+ NOTREACHED();
+ }
+ return StringPrintf("CellularStateUnknown-%d", state);
+}
+
+// static
+string Cellular::GetModemStateString(ModemState modem_state) {
+ switch (modem_state) {
+ case kModemStateFailed:
+ return "CellularModemStateFailed";
+ case kModemStateUnknown:
+ return "CellularModemStateUnknown";
+ case kModemStateInitializing:
+ return "CellularModemStateInitializing";
+ case kModemStateLocked:
+ return "CellularModemStateLocked";
+ case kModemStateDisabled:
+ return "CellularModemStateDisabled";
+ case kModemStateDisabling:
+ return "CellularModemStateDisabling";
+ case kModemStateEnabling:
+ return "CellularModemStateEnabling";
+ case kModemStateEnabled:
+ return "CellularModemStateEnabled";
+ case kModemStateSearching:
+ return "CellularModemStateSearching";
+ case kModemStateRegistered:
+ return "CellularModemStateRegistered";
+ case kModemStateDisconnecting:
+ return "CellularModemStateDisconnecting";
+ case kModemStateConnecting:
+ return "CellularModemStateConnecting";
+ case kModemStateConnected:
+ return "CellularModemStateConnected";
+ default:
+ NOTREACHED();
+ }
+ return StringPrintf("CellularModemStateUnknown-%d", modem_state);
+}
+
+string Cellular::GetTechnologyFamily(Error *error) {
+ return capability_->GetTypeString();
+}
+
+void Cellular::SetState(State state) {
+ SLOG(this, 2) << GetStateString(state_) << " -> "
+ << GetStateString(state);
+ state_ = state;
+}
+
+void Cellular::HelpRegisterDerivedBool(
+ const string &name,
+ bool(Cellular::*get)(Error *error),
+ bool(Cellular::*set)(const bool &value, Error *error)) {
+ mutable_store()->RegisterDerivedBool(
+ name,
+ BoolAccessor(
+ new CustomAccessor<Cellular, bool>(this, get, set)));
+}
+
+void Cellular::HelpRegisterConstDerivedString(
+ const string &name,
+ string(Cellular::*get)(Error *)) {
+ mutable_store()->RegisterDerivedString(
+ name,
+ StringAccessor(new CustomAccessor<Cellular, string>(this, get, nullptr)));
+}
+
+void Cellular::Start(Error *error,
+ const EnabledStateChangedCallback &callback) {
+ DCHECK(error);
+ SLOG(this, 2) << __func__ << ": " << GetStateString(state_);
+ // We can only short circuit the start operation if both the cellular state
+ // is not disabled AND the proxies have been initialized. We have seen
+ // crashes due to NULL proxies and the state being not disabled.
+ if (state_ != kStateDisabled && capability_->AreProxiesInitialized()) {
+ return;
+ }
+
+ ResultCallback cb = Bind(&Cellular::StartModemCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ capability_->StartModem(error, cb);
+}
+
+void Cellular::Stop(Error *error,
+ const EnabledStateChangedCallback &callback) {
+ SLOG(this, 2) << __func__ << ": " << GetStateString(state_);
+ explicit_disconnect_ = true;
+ ResultCallback cb = Bind(&Cellular::StopModemCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ capability_->StopModem(error, cb);
+}
+
+bool Cellular::IsUnderlyingDeviceEnabled() const {
+ return IsEnabledModemState(modem_state_);
+}
+
+bool Cellular::IsModemRegistered() const {
+ return (modem_state_ == Cellular::kModemStateRegistered ||
+ modem_state_ == Cellular::kModemStateConnecting ||
+ modem_state_ == Cellular::kModemStateConnected);
+}
+
+// static
+bool Cellular::IsEnabledModemState(ModemState state) {
+ switch (state) {
+ case kModemStateFailed:
+ case kModemStateUnknown:
+ case kModemStateDisabled:
+ case kModemStateInitializing:
+ case kModemStateLocked:
+ case kModemStateDisabling:
+ case kModemStateEnabling:
+ return false;
+ case kModemStateEnabled:
+ case kModemStateSearching:
+ case kModemStateRegistered:
+ case kModemStateDisconnecting:
+ case kModemStateConnecting:
+ case kModemStateConnected:
+ return true;
+ }
+ return false;
+}
+
+void Cellular::StartModemCallback(const EnabledStateChangedCallback &callback,
+ const Error &error) {
+ SLOG(this, 2) << __func__ << ": " << GetStateString(state_);
+ if (error.IsSuccess() && (state_ == kStateDisabled)) {
+ SetState(kStateEnabled);
+ // Registration state updates may have been ignored while the
+ // modem was not yet marked enabled.
+ HandleNewRegistrationState();
+ }
+ callback.Run(error);
+}
+
+void Cellular::StopModemCallback(const EnabledStateChangedCallback &callback,
+ const Error &error) {
+ SLOG(this, 2) << __func__ << ": " << GetStateString(state_);
+ explicit_disconnect_ = false;
+ // Destroy the cellular service regardless of any errors that occur during
+ // the stop process since we do not know the state of the modem at this
+ // point.
+ DestroyService();
+ if (state_ != kStateDisabled)
+ SetState(kStateDisabled);
+ callback.Run(error);
+ // In case no termination action was executed (and TerminationActionComplete
+ // was not invoked) in response to a suspend request, any registered
+ // termination action needs to be removed explicitly.
+ manager()->RemoveTerminationAction(FriendlyName());
+}
+
+void Cellular::InitCapability(Type type) {
+ // TODO(petkov): Consider moving capability construction into a factory that's
+ // external to the Cellular class.
+ SLOG(this, 2) << __func__ << "(" << type << ")";
+ switch (type) {
+ case kTypeGSM:
+ capability_.reset(new CellularCapabilityGSM(this,
+ proxy_factory_,
+ modem_info_));
+ break;
+ case kTypeCDMA:
+ capability_.reset(new CellularCapabilityCDMA(this,
+ proxy_factory_,
+ modem_info_));
+ break;
+ case kTypeUniversal:
+ capability_.reset(new CellularCapabilityUniversal(
+ this,
+ proxy_factory_,
+ modem_info_));
+ break;
+ case kTypeUniversalCDMA:
+ capability_.reset(new CellularCapabilityUniversalCDMA(
+ this,
+ proxy_factory_,
+ modem_info_));
+ break;
+ default: NOTREACHED();
+ }
+ mobile_operator_info_observer_->set_capability(capability_.get());
+}
+
+void Cellular::Activate(const string &carrier,
+ Error *error, const ResultCallback &callback) {
+ capability_->Activate(carrier, error, callback);
+}
+
+void Cellular::CompleteActivation(Error *error) {
+ capability_->CompleteActivation(error);
+}
+
+void Cellular::RegisterOnNetwork(const string &network_id,
+ Error *error,
+ const ResultCallback &callback) {
+ capability_->RegisterOnNetwork(network_id, error, callback);
+}
+
+void Cellular::RequirePIN(const string &pin, bool require,
+ Error *error, const ResultCallback &callback) {
+ SLOG(this, 2) << __func__ << "(" << require << ")";
+ capability_->RequirePIN(pin, require, error, callback);
+}
+
+void Cellular::EnterPIN(const string &pin,
+ Error *error, const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ capability_->EnterPIN(pin, error, callback);
+}
+
+void Cellular::UnblockPIN(const string &unblock_code,
+ const string &pin,
+ Error *error, const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ capability_->UnblockPIN(unblock_code, pin, error, callback);
+}
+
+void Cellular::ChangePIN(const string &old_pin, const string &new_pin,
+ Error *error, const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ capability_->ChangePIN(old_pin, new_pin, error, callback);
+}
+
+void Cellular::Reset(Error *error, const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ capability_->Reset(error, callback);
+}
+
+void Cellular::SetCarrier(const string &carrier,
+ Error *error, const ResultCallback &callback) {
+ SLOG(this, 2) << __func__ << "(" << carrier << ")";
+ capability_->SetCarrier(carrier, error, callback);
+}
+
+bool Cellular::IsIPv6Allowed() const {
+ // A cellular device is disabled before the system goes into suspend mode.
+ // However, outstanding TCP sockets may not be nuked when the associated
+ // network interface goes down. When the system resumes from suspend, the
+ // cellular device is re-enabled and may reconnect to the network, which
+ // acquire a new IPv6 address on the network interface. However, those
+ // outstanding TCP sockets may initiate traffic with the old IPv6 address.
+ // Some network may not like the fact that two IPv6 addresses originated from
+ // the same modem within a connection session and may drop the connection.
+ // Here we disable IPv6 support on cellular devices to work around the issue.
+ //
+ // TODO(benchan): Resolve the IPv6 issue in a different way and then
+ // re-enable IPv6 support on cellular devices.
+ return false;
+}
+
+void Cellular::DropConnection() {
+ if (ppp_device_) {
+ // For PPP dongles, IP configuration is handled on the |ppp_device_|,
+ // rather than the netdev plumbed into |this|.
+ ppp_device_->DropConnection();
+ } else {
+ Device::DropConnection();
+ }
+}
+
+void Cellular::SetServiceState(Service::ConnectState state) {
+ if (ppp_device_) {
+ ppp_device_->SetServiceState(state);
+ } else if (selected_service()) {
+ Device::SetServiceState(state);
+ } else if (service_) {
+ service_->SetState(state);
+ } else {
+ LOG(WARNING) << "State change with no Service.";
+ }
+}
+
+void Cellular::SetServiceFailure(Service::ConnectFailure failure_state) {
+ if (ppp_device_) {
+ ppp_device_->SetServiceFailure(failure_state);
+ } else if (selected_service()) {
+ Device::SetServiceFailure(failure_state);
+ } else if (service_) {
+ service_->SetFailure(failure_state);
+ } else {
+ LOG(WARNING) << "State change with no Service.";
+ }
+}
+
+void Cellular::SetServiceFailureSilent(Service::ConnectFailure failure_state) {
+ if (ppp_device_) {
+ ppp_device_->SetServiceFailureSilent(failure_state);
+ } else if (selected_service()) {
+ Device::SetServiceFailureSilent(failure_state);
+ } else if (service_) {
+ service_->SetFailureSilent(failure_state);
+ } else {
+ LOG(WARNING) << "State change with no Service.";
+ }
+}
+
+void Cellular::OnBeforeSuspend(const ResultCallback &callback) {
+ LOG(INFO) << __func__;
+ Error error;
+ StopPPP();
+ SetEnabledNonPersistent(false, &error, callback);
+ if (error.IsFailure() && error.type() != Error::kInProgress) {
+ // If we fail to disable the modem right away, proceed instead of wasting
+ // the time to wait for the suspend/termination delay to expire.
+ LOG(WARNING) << "Proceed with suspend/termination even though the modem "
+ << "is not yet disabled: " << error;
+ callback.Run(error);
+ }
+}
+
+void Cellular::OnAfterResume() {
+ SLOG(this, 2) << __func__;
+ if (enabled_persistent()) {
+ LOG(INFO) << "Restarting modem after resume.";
+
+ // If we started disabling the modem before suspend, but that
+ // suspend is still in progress, then we are not yet in
+ // kStateDisabled. That's a problem, because Cellular::Start
+ // returns immediately in that case. Hack around that by forcing
+ // |state_| here.
+ //
+ // TODO(quiche): Remove this hack. Maybe
+ // CellularCapabilityUniversal should generate separate
+ // notifications for Stop_Disable, and Stop_PowerDown. Then we'd
+ // update our state to kStateDisabled when Stop_Disable completes.
+ state_ = kStateDisabled;
+
+ Error error;
+ SetEnabledUnchecked(true, &error, Bind(LogRestartModemResult));
+ if (error.IsSuccess()) {
+ LOG(INFO) << "Modem restart completed immediately.";
+ } else if (error.IsOngoing()) {
+ LOG(INFO) << "Modem restart in progress.";
+ } else {
+ LOG(WARNING) << "Modem restart failed: " << error;
+ }
+ }
+ // TODO(quiche): Consider if this should be conditional. If, e.g.,
+ // the device was still disabling when we suspended, will trying to
+ // renew DHCP here cause problems?
+ Device::OnAfterResume();
+}
+
+void Cellular::Scan(ScanType /*scan_type*/, Error *error,
+ const string &/*reason*/) {
+ SLOG(this, 2) << __func__;
+ CHECK(error);
+ if (proposed_scan_in_progress_) {
+ Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
+ return;
+ }
+
+ // |scan_type| is ignored because Cellular only does a full scan.
+ ResultStringmapsCallback cb = Bind(&Cellular::OnScanReply,
+ weak_ptr_factory_.GetWeakPtr());
+ capability_->Scan(error, cb);
+ // An immediate failure in |cabapility_->Scan(...)| is indicated through the
+ // |error| argument.
+ if (error->IsFailure())
+ return;
+
+ proposed_scan_in_progress_ = true;
+ UpdateScanning();
+}
+
+void Cellular::OnScanReply(const Stringmaps &found_networks,
+ const Error &error) {
+ proposed_scan_in_progress_ = false;
+ UpdateScanning();
+
+ // TODO(jglasgow): fix error handling.
+ // At present, there is no way of notifying user of this asynchronous error.
+ if (error.IsFailure()) {
+ clear_found_networks();
+ return;
+ }
+
+ set_found_networks(found_networks);
+}
+
+void Cellular::HandleNewRegistrationState() {
+ SLOG(this, 2) << __func__
+ << ": (new state " << GetStateString(state_) << ")";
+ if (!capability_->IsRegistered()) {
+ if (!explicit_disconnect_ &&
+ (state_ == kStateLinked || state_ == kStateConnected) &&
+ service_.get())
+ metrics()->NotifyCellularDeviceDrop(
+ capability_->GetNetworkTechnologyString(), service_->strength());
+ DestroyService();
+ if (state_ == kStateLinked ||
+ state_ == kStateConnected ||
+ state_ == kStateRegistered) {
+ SetState(kStateEnabled);
+ }
+ return;
+ }
+ // In Disabled state, defer creating a service until fully
+ // enabled. UI will ignore the appearance of a new service
+ // on a disabled device.
+ if (state_ == kStateDisabled) {
+ return;
+ }
+ if (state_ == kStateEnabled) {
+ SetState(kStateRegistered);
+ }
+ if (!service_.get()) {
+ metrics()->NotifyDeviceScanFinished(interface_index());
+ CreateService();
+ }
+ capability_->GetSignalQuality();
+ if (state_ == kStateRegistered && modem_state_ == kModemStateConnected)
+ OnConnected();
+ service_->SetNetworkTechnology(capability_->GetNetworkTechnologyString());
+ service_->SetRoamingState(capability_->GetRoamingStateString());
+ manager()->UpdateService(service_);
+}
+
+void Cellular::HandleNewSignalQuality(uint32_t strength) {
+ SLOG(this, 2) << "Signal strength: " << strength;
+ if (service_) {
+ service_->SetStrength(strength);
+ }
+}
+
+void Cellular::CreateService() {
+ SLOG(this, 2) << __func__;
+ CHECK(!service_.get());
+ service_ = new CellularService(modem_info_, this);
+ capability_->OnServiceCreated();
+
+ // Storage identifier must be set only once, and before registering the
+ // service with the manager, since we key off of this identifier to
+ // determine the profile to load.
+ // TODO(pprabhu) Make profile matching more robust (crbug.com/369755)
+ string service_id;
+ if (home_provider_info_->IsMobileNetworkOperatorKnown() &&
+ !home_provider_info_->uuid().empty()) {
+ service_id = home_provider_info_->uuid();
+ } else if (serving_operator_info_->IsMobileNetworkOperatorKnown() &&
+ !serving_operator_info_->uuid().empty()) {
+ service_id = serving_operator_info_->uuid();
+ } else {
+ switch (type_) {
+ case kTypeGSM:
+ case kTypeUniversal:
+ if (!sim_identifier().empty()) {
+ service_id = sim_identifier();
+ }
+ break;
+
+ case kTypeCDMA:
+ case kTypeUniversalCDMA:
+ if (!meid().empty()) {
+ service_id = meid();
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ }
+
+ if (!service_id.empty()) {
+ string storage_id = base::StringPrintf(
+ "%s_%s_%s",
+ kTypeCellular, address().c_str(), service_id.c_str());
+ service()->SetStorageIdentifier(storage_id);
+ }
+
+ manager()->RegisterService(service_);
+
+ // We might have missed a property update because the service wasn't created
+ // ealier.
+ UpdateScanning();
+ mobile_operator_info_observer_->OnOperatorChanged();
+}
+
+void Cellular::DestroyService() {
+ SLOG(this, 2) << __func__;
+ DropConnection();
+ if (service_) {
+ LOG(INFO) << "Deregistering cellular service " << service_->unique_name()
+ << " for device " << link_name();
+ manager()->DeregisterService(service_);
+ service_ = nullptr;
+ }
+}
+
+void Cellular::Connect(Error *error) {
+ SLOG(this, 2) << __func__;
+ if (state_ == kStateConnected || state_ == kStateLinked) {
+ Error::PopulateAndLog(error, Error::kAlreadyConnected,
+ "Already connected; connection request ignored.");
+ return;
+ } else if (state_ != kStateRegistered) {
+ Error::PopulateAndLog(error, Error::kNotRegistered,
+ "Modem not registered; connection request ignored.");
+ return;
+ }
+
+ if (!capability_->AllowRoaming() &&
+ service_->roaming_state() == kRoamingStateRoaming) {
+ Error::PopulateAndLog(error, Error::kNotOnHomeNetwork,
+ "Roaming disallowed; connection request ignored.");
+ return;
+ }
+
+ DBusPropertiesMap properties;
+ capability_->SetupConnectProperties(&properties);
+ ResultCallback cb = Bind(&Cellular::OnConnectReply,
+ weak_ptr_factory_.GetWeakPtr());
+ OnConnecting();
+ capability_->Connect(properties, error, cb);
+ if (!error->IsSuccess())
+ return;
+
+ bool is_auto_connecting = service_.get() && service_->is_auto_connecting();
+ metrics()->NotifyDeviceConnectStarted(interface_index(), is_auto_connecting);
+}
+
+// Note that there's no ResultCallback argument to this,
+// since Connect() isn't yet passed one.
+void Cellular::OnConnectReply(const Error &error) {
+ SLOG(this, 2) << __func__ << "(" << error << ")";
+ if (error.IsSuccess()) {
+ metrics()->NotifyDeviceConnectFinished(interface_index());
+ OnConnected();
+ } else {
+ metrics()->NotifyCellularDeviceFailure(error);
+ OnConnectFailed(error);
+ }
+}
+
+void Cellular::OnDisabled() {
+ SetEnabled(false);
+}
+
+void Cellular::OnEnabled() {
+ manager()->AddTerminationAction(FriendlyName(),
+ Bind(&Cellular::StartTermination,
+ weak_ptr_factory_.GetWeakPtr()));
+ SetEnabled(true);
+}
+
+void Cellular::OnConnecting() {
+ if (service_)
+ service_->SetState(Service::kStateAssociating);
+}
+
+void Cellular::OnConnected() {
+ SLOG(this, 2) << __func__;
+ if (state_ == kStateConnected || state_ == kStateLinked) {
+ SLOG(this, 2) << "Already connected";
+ return;
+ }
+ SetState(kStateConnected);
+ if (!service_) {
+ LOG(INFO) << "Disconnecting due to no cellular service.";
+ Disconnect(nullptr, "no celluar service");
+ } else if (!capability_->AllowRoaming() &&
+ service_->roaming_state() == kRoamingStateRoaming) {
+ LOG(INFO) << "Disconnecting due to roaming.";
+ Disconnect(nullptr, "roaming");
+ } else {
+ EstablishLink();
+ }
+}
+
+void Cellular::OnConnectFailed(const Error &error) {
+ if (service_)
+ service_->SetFailure(Service::kFailureUnknown);
+}
+
+void Cellular::Disconnect(Error *error, const char *reason) {
+ SLOG(this, 2) << __func__ << ": " << reason;
+ if (state_ != kStateConnected && state_ != kStateLinked) {
+ Error::PopulateAndLog(
+ error, Error::kNotConnected, "Not connected; request ignored.");
+ return;
+ }
+ StopPPP();
+ explicit_disconnect_ = true;
+ ResultCallback cb = Bind(&Cellular::OnDisconnectReply,
+ weak_ptr_factory_.GetWeakPtr());
+ capability_->Disconnect(error, cb);
+}
+
+void Cellular::OnDisconnectReply(const Error &error) {
+ SLOG(this, 2) << __func__ << "(" << error << ")";
+ explicit_disconnect_ = false;
+ if (error.IsSuccess()) {
+ OnDisconnected();
+ } else {
+ metrics()->NotifyCellularDeviceFailure(error);
+ OnDisconnectFailed();
+ }
+}
+
+void Cellular::OnDisconnected() {
+ SLOG(this, 2) << __func__;
+ if (!DisconnectCleanup()) {
+ LOG(WARNING) << "Disconnect occurred while in state "
+ << GetStateString(state_);
+ }
+}
+
+void Cellular::OnDisconnectFailed() {
+ SLOG(this, 2) << __func__;
+ // If the modem is in the disconnecting state, then
+ // the disconnect should eventually succeed, so do
+ // nothing.
+ if (modem_state_ == kModemStateDisconnecting) {
+ LOG(WARNING) << "Ignoring failed disconnect while modem is disconnecting.";
+ return;
+ }
+
+ // OnDisconnectFailed got called because no bearers
+ // to disconnect were found. Which means that we shouldn't
+ // really remain in the connected/linked state if we
+ // are in one of those.
+ if (!DisconnectCleanup()) {
+ // otherwise, no-op
+ LOG(WARNING) << "Ignoring failed disconnect while in state "
+ << GetStateString(state_);
+ }
+
+ // TODO(armansito): In either case, shill ends up thinking
+ // that it's disconnected, while for some reason the underlying
+ // modem might still actually be connected. In that case the UI
+ // would be reflecting an incorrect state and a further connection
+ // request would fail. We should perhaps tear down the modem and
+ // restart it here.
+}
+
+void Cellular::EstablishLink() {
+ SLOG(this, 2) << __func__;
+ CHECK_EQ(kStateConnected, state_);
+
+ CellularBearer *bearer = capability_->GetActiveBearer();
+ if (bearer && bearer->ipv4_config_method() == IPConfig::kMethodPPP) {
+ LOG(INFO) << "Start PPP connection on " << bearer->data_interface();
+ StartPPP(bearer->data_interface());
+ return;
+ }
+
+ unsigned int flags = 0;
+ if (manager()->device_info()->GetFlags(interface_index(), &flags) &&
+ (flags & IFF_UP) != 0) {
+ LinkEvent(flags, IFF_UP);
+ return;
+ }
+ // TODO(petkov): Provide a timeout for a failed link-up request.
+ rtnl_handler()->SetInterfaceFlags(interface_index(), IFF_UP, IFF_UP);
+
+ // Set state to associating.
+ OnConnecting();
+}
+
+void Cellular::LinkEvent(unsigned int flags, unsigned int change) {
+ Device::LinkEvent(flags, change);
+ if (ppp_task_) {
+ LOG(INFO) << "Ignoring LinkEvent on device with PPP interface.";
+ return;
+ }
+
+ if ((flags & IFF_UP) != 0 && state_ == kStateConnected) {
+ LOG(INFO) << link_name() << " is up.";
+ SetState(kStateLinked);
+
+ // TODO(benchan): IPv6 support is currently disabled for cellular devices.
+ // Check and obtain IPv6 configuration from the bearer when we later enable
+ // IPv6 support on cellular devices.
+ CellularBearer *bearer = capability_->GetActiveBearer();
+ if (bearer && bearer->ipv4_config_method() == IPConfig::kMethodStatic) {
+ SLOG(this, 2) << "Assign static IP configuration from bearer.";
+ SelectService(service_);
+ SetServiceState(Service::kStateConfiguring);
+ AssignIPConfig(*bearer->ipv4_config_properties());
+ return;
+ }
+
+ if (AcquireIPConfig()) {
+ SLOG(this, 2) << "Start DHCP to acquire IP configuration.";
+ SelectService(service_);
+ SetServiceState(Service::kStateConfiguring);
+ return;
+ }
+
+ LOG(ERROR) << "Unable to acquire IP configuration over DHCP.";
+ return;
+ }
+
+ if ((flags & IFF_UP) == 0 && state_ == kStateLinked) {
+ LOG(INFO) << link_name() << " is down.";
+ SetState(kStateConnected);
+ DropConnection();
+ }
+}
+
+void Cellular::OnDBusPropertiesChanged(
+ const string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const vector<string> &invalidated_properties) {
+ capability_->OnDBusPropertiesChanged(interface,
+ changed_properties,
+ invalidated_properties);
+}
+
+string Cellular::CreateDefaultFriendlyServiceName() {
+ SLOG(this, 2) << __func__;
+ return base::StringPrintf("%s_%u",
+ kGenericServiceNamePrefix,
+ friendly_service_name_id_++);
+}
+
+bool Cellular::IsDefaultFriendlyServiceName(const string &service_name) const {
+ return StartsWithASCII(service_name, kGenericServiceNamePrefix, true);
+}
+
+void Cellular::OnModemStateChanged(ModemState new_state) {
+ ModemState old_state = modem_state_;
+ SLOG(this, 2) << __func__ << ": " << GetModemStateString(old_state)
+ << " -> " << GetModemStateString(new_state);
+ if (old_state == new_state) {
+ SLOG(this, 2) << "The new state matches the old state. Nothing to do.";
+ return;
+ }
+ set_modem_state(new_state);
+ if (old_state >= kModemStateRegistered &&
+ new_state < kModemStateRegistered) {
+ capability_->SetUnregistered(new_state == kModemStateSearching);
+ HandleNewRegistrationState();
+ }
+ if (new_state == kModemStateDisabled) {
+ OnDisabled();
+ } else if (new_state >= kModemStateEnabled) {
+ if (old_state < kModemStateEnabled) {
+ // Just became enabled, update enabled state.
+ OnEnabled();
+ }
+ if ((new_state == kModemStateEnabled ||
+ new_state == kModemStateSearching ||
+ new_state == kModemStateRegistered) &&
+ (old_state == kModemStateConnected ||
+ old_state == kModemStateConnecting ||
+ old_state == kModemStateDisconnecting))
+ OnDisconnected();
+ else if (new_state == kModemStateConnecting)
+ OnConnecting();
+ else if (new_state == kModemStateConnected &&
+ old_state == kModemStateConnecting)
+ OnConnected();
+ }
+
+ // Update the kScanningProperty property after we've handled the current state
+ // update completely.
+ UpdateScanning();
+}
+
+bool Cellular::IsActivating() const {
+ return capability_->IsActivating();
+}
+
+bool Cellular::SetAllowRoaming(const bool &value, Error */*error*/) {
+ SLOG(this, 2) << __func__
+ << "(" << allow_roaming_ << "->" << value << ")";
+ if (allow_roaming_ == value) {
+ return false;
+ }
+ allow_roaming_ = value;
+ manager()->UpdateDevice(this);
+
+ // Use AllowRoaming() instead of allow_roaming_ in order to
+ // incorporate provider preferences when evaluating if a disconnect
+ // is required.
+ if (!capability_->AllowRoaming() &&
+ capability_->GetRoamingStateString() == kRoamingStateRoaming) {
+ Error error;
+ Disconnect(&error, __func__);
+ }
+ adaptor()->EmitBoolChanged(kCellularAllowRoamingProperty, value);
+ return true;
+}
+
+void Cellular::StartTermination() {
+ SLOG(this, 2) << __func__;
+ OnBeforeSuspend(Bind(&Cellular::OnTerminationCompleted,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void Cellular::OnTerminationCompleted(const Error &error) {
+ LOG(INFO) << __func__ << ": " << error;
+ manager()->TerminationActionComplete(FriendlyName());
+ manager()->RemoveTerminationAction(FriendlyName());
+}
+
+bool Cellular::DisconnectCleanup() {
+ bool succeeded = false;
+ if (state_ == kStateConnected || state_ == kStateLinked) {
+ SetState(kStateRegistered);
+ SetServiceFailureSilent(Service::kFailureUnknown);
+ DestroyIPConfig();
+ succeeded = true;
+ }
+ capability_->DisconnectCleanup();
+ return succeeded;
+}
+
+// static
+void Cellular::LogRestartModemResult(const Error &error) {
+ if (error.IsSuccess()) {
+ LOG(INFO) << "Modem restart completed.";
+ } else {
+ LOG(WARNING) << "Attempt to restart modem failed: " << error;
+ }
+}
+
+void Cellular::StartPPP(const string &serial_device) {
+ SLOG(PPP, this, 2) << __func__ << " on " << serial_device;
+ // Detach any SelectedService from this device. It will be grafted onto
+ // the PPPDevice after PPP is up (in Cellular::Notify).
+ //
+ // This has two important effects: 1) kills dhcpcd if it is running.
+ // 2) stops Cellular::LinkEvent from driving changes to the
+ // SelectedService.
+ if (selected_service()) {
+ CHECK_EQ(service_.get(), selected_service().get());
+ // Save and restore |service_| state, as DropConnection calls
+ // SelectService, and SelectService will move selected_service()
+ // to kStateIdle.
+ Service::ConnectState original_state(service_->state());
+ Device::DropConnection(); // Don't redirect to PPPDevice.
+ service_->SetState(original_state);
+ } else {
+ CHECK(!ipconfig()); // Shouldn't have ipconfig without selected_service().
+ }
+
+ base::Callback<void(pid_t, int)> death_callback(
+ Bind(&Cellular::OnPPPDied, weak_ptr_factory_.GetWeakPtr()));
+ vector<string> args;
+ map<string, string> environment;
+ Error error;
+ if (SLOG_IS_ON(PPP, 5)) {
+ args.push_back("debug");
+ }
+ args.push_back("nodetach");
+ args.push_back("nodefaultroute"); // Don't let pppd muck with routing table.
+ args.push_back("usepeerdns"); // Request DNS servers.
+ args.push_back("plugin"); // Goes with next arg.
+ args.push_back(PPPDevice::kPluginPath);
+ args.push_back(serial_device);
+ is_ppp_authenticating_ = false;
+ std::unique_ptr<ExternalTask> new_ppp_task(
+ new ExternalTask(modem_info_->control_interface(),
+ modem_info_->glib(),
+ weak_ptr_factory_.GetWeakPtr(),
+ death_callback));
+ if (new_ppp_task->Start(
+ FilePath(PPPDevice::kDaemonPath), args, environment, true, &error)) {
+ LOG(INFO) << "Forked pppd process.";
+ ppp_task_ = std::move(new_ppp_task);
+ }
+}
+
+void Cellular::StopPPP() {
+ SLOG(PPP, this, 2) << __func__;
+ if (!ppp_task_) {
+ return;
+ }
+ DropConnection();
+ ppp_task_.reset();
+ ppp_device_ = nullptr;
+}
+
+// called by |ppp_task_|
+void Cellular::GetLogin(string *user, string *password) {
+ SLOG(PPP, this, 2) << __func__;
+ if (!service()) {
+ LOG(ERROR) << __func__ << " with no service ";
+ return;
+ }
+ CHECK(user);
+ CHECK(password);
+ *user = service()->ppp_username();
+ *password = service()->ppp_password();
+}
+
+// Called by |ppp_task_|.
+void Cellular::Notify(const string &reason,
+ const map<string, string> &dict) {
+ SLOG(PPP, this, 2) << __func__ << " " << reason << " on " << link_name();
+
+ if (reason == kPPPReasonAuthenticating) {
+ OnPPPAuthenticating();
+ } else if (reason == kPPPReasonAuthenticated) {
+ OnPPPAuthenticated();
+ } else if (reason == kPPPReasonConnect) {
+ OnPPPConnected(dict);
+ } else if (reason == kPPPReasonDisconnect) {
+ OnPPPDisconnected();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void Cellular::OnPPPAuthenticated() {
+ SLOG(PPP, this, 2) << __func__;
+ is_ppp_authenticating_ = false;
+}
+
+void Cellular::OnPPPAuthenticating() {
+ SLOG(PPP, this, 2) << __func__;
+ is_ppp_authenticating_ = true;
+}
+
+void Cellular::OnPPPConnected(const map<string, string> ¶ms) {
+ SLOG(PPP, this, 2) << __func__;
+ string interface_name = PPPDevice::GetInterfaceName(params);
+ DeviceInfo *device_info = modem_info_->manager()->device_info();
+ int interface_index = device_info->GetIndex(interface_name);
+ if (interface_index < 0) {
+ // TODO(quiche): Consider handling the race when the RTNL notification about
+ // the new PPP device has not been received yet. crbug.com/246832.
+ NOTIMPLEMENTED() << ": No device info for " << interface_name << ".";
+ return;
+ }
+
+ if (!ppp_device_ || ppp_device_->interface_index() != interface_index) {
+ if (ppp_device_) {
+ ppp_device_->SelectService(nullptr); // No longer drives |service_|.
+ }
+ ppp_device_ = ppp_device_factory_->CreatePPPDevice(
+ modem_info_->control_interface(),
+ modem_info_->dispatcher(),
+ modem_info_->metrics(),
+ modem_info_->manager(),
+ interface_name,
+ interface_index);
+ device_info->RegisterDevice(ppp_device_);
+ }
+
+ CHECK(service_);
+ // For PPP, we only SelectService on the |ppp_device_|.
+ CHECK(!selected_service());
+ const bool kBlackholeIPv6 = false;
+ ppp_device_->SetEnabled(true);
+ ppp_device_->SelectService(service_);
+ ppp_device_->UpdateIPConfigFromPPP(params, kBlackholeIPv6);
+}
+
+void Cellular::OnPPPDisconnected() {
+ SLOG(PPP, this, 2) << __func__;
+ // DestroyLater, rather than while on stack.
+ ppp_task_.release()->DestroyLater(modem_info_->dispatcher());
+ if (is_ppp_authenticating_) {
+ SetServiceFailure(Service::kFailurePPPAuth);
+ } else {
+ // TODO(quiche): Don't set failure if we disconnected intentionally.
+ SetServiceFailure(Service::kFailureUnknown);
+ }
+ Error error;
+ Disconnect(&error, __func__);
+}
+
+void Cellular::OnPPPDied(pid_t pid, int exit) {
+ LOG(INFO) << __func__ << " on " << link_name();
+ OnPPPDisconnected();
+}
+
+void Cellular::UpdateScanning() {
+ if (proposed_scan_in_progress_) {
+ set_scanning(true);
+ return;
+ }
+
+ if (modem_state_ == kModemStateEnabling) {
+ set_scanning(true);
+ return;
+ }
+
+ if (service_ && service_->activation_state() != kActivationStateActivated) {
+ set_scanning(false);
+ return;
+ }
+
+ if (modem_state_ == kModemStateEnabled ||
+ modem_state_ == kModemStateSearching) {
+ set_scanning(true);
+ return;
+ }
+
+ set_scanning(false);
+}
+
+void Cellular::RegisterProperties() {
+ PropertyStore *store = this->mutable_store();
+
+ // These properties do not have setters, and events are not generated when
+ // they are changed.
+ store->RegisterConstString(kDBusServiceProperty, &dbus_service_);
+ store->RegisterConstString(kDBusObjectProperty, &dbus_path_);
+
+ store->RegisterUint16(kScanIntervalProperty, &scan_interval_);
+
+ // These properties have setters that should be used to change their values.
+ // Events are generated whenever the values change.
+ store->RegisterConstStringmap(kHomeProviderProperty, &home_provider_);
+ store->RegisterConstString(kCarrierProperty, &carrier_);
+ store->RegisterConstBool(kSupportNetworkScanProperty, &scanning_supported_);
+ store->RegisterConstString(kEsnProperty, &esn_);
+ store->RegisterConstString(kFirmwareRevisionProperty, &firmware_revision_);
+ store->RegisterConstString(kHardwareRevisionProperty, &hardware_revision_);
+ store->RegisterConstString(kImeiProperty, &imei_);
+ store->RegisterConstString(kImsiProperty, &imsi_);
+ store->RegisterConstString(kMdnProperty, &mdn_);
+ store->RegisterConstString(kMeidProperty, &meid_);
+ store->RegisterConstString(kMinProperty, &min_);
+ store->RegisterConstString(kManufacturerProperty, &manufacturer_);
+ store->RegisterConstString(kModelIDProperty, &model_id_);
+ store->RegisterConstBool(kScanningProperty, &scanning_);
+
+ store->RegisterConstString(kSelectedNetworkProperty, &selected_network_);
+ store->RegisterConstStringmaps(kFoundNetworksProperty, &found_networks_);
+ store->RegisterConstBool(kProviderRequiresRoamingProperty,
+ &provider_requires_roaming_);
+ store->RegisterConstBool(kSIMPresentProperty, &sim_present_);
+ store->RegisterConstStringmaps(kCellularApnListProperty, &apn_list_);
+ store->RegisterConstString(kIccidProperty, &sim_identifier_);
+
+ store->RegisterConstStrings(kSupportedCarriersProperty, &supported_carriers_);
+ store->RegisterConstUint16(kPRLVersionProperty, &prl_version_);
+
+ // TODO(pprabhu): Decide whether these need their own custom setters.
+ HelpRegisterConstDerivedString(kTechnologyFamilyProperty,
+ &Cellular::GetTechnologyFamily);
+ HelpRegisterDerivedBool(kCellularAllowRoamingProperty,
+ &Cellular::GetAllowRoaming,
+ &Cellular::SetAllowRoaming);
+}
+
+void Cellular::set_home_provider(const Stringmap &home_provider) {
+ if (home_provider_ == home_provider)
+ return;
+
+ home_provider_ = home_provider;
+ adaptor()->EmitStringmapChanged(kHomeProviderProperty, home_provider_);
+}
+
+void Cellular::set_carrier(const string &carrier) {
+ if (carrier_ == carrier)
+ return;
+
+ carrier_ = carrier;
+ adaptor()->EmitStringChanged(kCarrierProperty, carrier_);
+}
+
+void Cellular::set_scanning_supported(bool scanning_supported) {
+ if (scanning_supported_ == scanning_supported)
+ return;
+
+ scanning_supported_ = scanning_supported;
+ if (adaptor())
+ adaptor()->EmitBoolChanged(kSupportNetworkScanProperty,
+ scanning_supported_);
+ else
+ SLOG(this, 2) << "Could not emit signal for property |"
+ << kSupportNetworkScanProperty
+ << "| change. DBus adaptor is NULL!";
+}
+
+void Cellular::set_esn(const string &esn) {
+ if (esn_ == esn)
+ return;
+
+ esn_ = esn;
+ adaptor()->EmitStringChanged(kEsnProperty, esn_);
+}
+
+void Cellular::set_firmware_revision(const string &firmware_revision) {
+ if (firmware_revision_ == firmware_revision)
+ return;
+
+ firmware_revision_ = firmware_revision;
+ adaptor()->EmitStringChanged(kFirmwareRevisionProperty, firmware_revision_);
+}
+
+void Cellular::set_hardware_revision(const string &hardware_revision) {
+ if (hardware_revision_ == hardware_revision)
+ return;
+
+ hardware_revision_ = hardware_revision;
+ adaptor()->EmitStringChanged(kHardwareRevisionProperty, hardware_revision_);
+}
+
+// TODO(armansito): The following methods should probably log their argument
+// values. Need to learn if any of them need to be scrubbed.
+void Cellular::set_imei(const string &imei) {
+ if (imei_ == imei)
+ return;
+
+ imei_ = imei;
+ adaptor()->EmitStringChanged(kImeiProperty, imei_);
+}
+
+void Cellular::set_imsi(const string &imsi) {
+ if (imsi_ == imsi)
+ return;
+
+ imsi_ = imsi;
+ adaptor()->EmitStringChanged(kImsiProperty, imsi_);
+}
+
+void Cellular::set_mdn(const string &mdn) {
+ if (mdn_ == mdn)
+ return;
+
+ mdn_ = mdn;
+ adaptor()->EmitStringChanged(kMdnProperty, mdn_);
+}
+
+void Cellular::set_meid(const string &meid) {
+ if (meid_ == meid)
+ return;
+
+ meid_ = meid;
+ adaptor()->EmitStringChanged(kMeidProperty, meid_);
+}
+
+void Cellular::set_min(const string &min) {
+ if (min_ == min)
+ return;
+
+ min_ = min;
+ adaptor()->EmitStringChanged(kMinProperty, min_);
+}
+
+void Cellular::set_manufacturer(const string &manufacturer) {
+ if (manufacturer_ == manufacturer)
+ return;
+
+ manufacturer_ = manufacturer;
+ adaptor()->EmitStringChanged(kManufacturerProperty, manufacturer_);
+}
+
+void Cellular::set_model_id(const string &model_id) {
+ if (model_id_ == model_id)
+ return;
+
+ model_id_ = model_id;
+ adaptor()->EmitStringChanged(kModelIDProperty, model_id_);
+}
+
+void Cellular::set_mm_plugin(const string &mm_plugin) {
+ mm_plugin_ = mm_plugin;
+}
+
+void Cellular::set_scanning(bool scanning) {
+ if (scanning_ == scanning)
+ return;
+
+ scanning_ = scanning;
+ adaptor()->EmitBoolChanged(kScanningProperty, scanning_);
+
+ // kScanningProperty is a sticky-false property.
+ // Every time it is set to |true|, it will remain |true| up to a maximum of
+ // |kScanningTimeout| time, after which it will be reset to |false|.
+ if (!scanning_ && !scanning_timeout_callback_.IsCancelled()) {
+ SLOG(this, 2) << "Scanning set to false. "
+ << "Cancelling outstanding timeout.";
+ scanning_timeout_callback_.Cancel();
+ } else {
+ CHECK(scanning_timeout_callback_.IsCancelled());
+ SLOG(this, 2) << "Scanning set to true. "
+ << "Starting timeout to reset to false.";
+ scanning_timeout_callback_.Reset(Bind(&Cellular::set_scanning,
+ weak_ptr_factory_.GetWeakPtr(),
+ false));
+ dispatcher()->PostDelayedTask(
+ scanning_timeout_callback_.callback(),
+ scanning_timeout_milliseconds_);
+ }
+}
+
+void Cellular::set_selected_network(const string &selected_network) {
+ if (selected_network_ == selected_network)
+ return;
+
+ selected_network_ = selected_network;
+ adaptor()->EmitStringChanged(kSelectedNetworkProperty, selected_network_);
+}
+
+void Cellular::set_found_networks(const Stringmaps &found_networks) {
+ // There is no canonical form of a Stringmaps value.
+ // So don't check for redundant updates.
+ found_networks_ = found_networks;
+ adaptor()->EmitStringmapsChanged(kFoundNetworksProperty, found_networks_);
+}
+
+void Cellular::clear_found_networks() {
+ if (found_networks_.empty())
+ return;
+
+ found_networks_.clear();
+ adaptor()->EmitStringmapsChanged(kFoundNetworksProperty, found_networks_);
+}
+
+void Cellular::set_provider_requires_roaming(bool provider_requires_roaming) {
+ if (provider_requires_roaming_ == provider_requires_roaming)
+ return;
+
+ provider_requires_roaming_ = provider_requires_roaming;
+ adaptor()->EmitBoolChanged(kProviderRequiresRoamingProperty,
+ provider_requires_roaming_);
+}
+
+void Cellular::set_sim_present(bool sim_present) {
+ if (sim_present_ == sim_present)
+ return;
+
+ sim_present_ = sim_present;
+ adaptor()->EmitBoolChanged(kSIMPresentProperty, sim_present_);
+}
+
+void Cellular::set_apn_list(const Stringmaps &apn_list) {
+ // There is no canonical form of a Stringmaps value.
+ // So don't check for redundant updates.
+ apn_list_ = apn_list;
+ // See crbug.com/215581: Sometimes adaptor may be nullptr when |set_apn_list|
+ // is called.
+ if (adaptor())
+ adaptor()->EmitStringmapsChanged(kCellularApnListProperty, apn_list_);
+ else
+ SLOG(this, 2) << "Could not emit signal for property |"
+ << kCellularApnListProperty
+ << "| change. DBus adaptor is NULL!";
+}
+
+void Cellular::set_sim_identifier(const string &sim_identifier) {
+ if (sim_identifier_ == sim_identifier)
+ return;
+
+ sim_identifier_ = sim_identifier;
+ adaptor()->EmitStringChanged(kIccidProperty, sim_identifier_);
+}
+
+void Cellular::set_supported_carriers(const Strings &supported_carriers) {
+ // There is no canonical form of a Strings value.
+ // So don't check for redundant updates.
+ supported_carriers_ = supported_carriers;
+ adaptor()->EmitStringsChanged(kSupportedCarriersProperty,
+ supported_carriers_);
+}
+
+void Cellular::set_prl_version(uint16_t prl_version) {
+ if (prl_version_ == prl_version)
+ return;
+
+ prl_version_ = prl_version;
+ adaptor()->EmitUint16Changed(kPRLVersionProperty, prl_version_);
+}
+
+void Cellular::set_home_provider_info(MobileOperatorInfo *home_provider_info) {
+ home_provider_info_.reset(home_provider_info);
+}
+
+void Cellular::set_serving_operator_info(
+ MobileOperatorInfo *serving_operator_info) {
+ serving_operator_info_.reset(serving_operator_info);
+}
+
+void Cellular::UpdateHomeProvider(const MobileOperatorInfo *operator_info) {
+ SLOG(this, 3) << __func__;
+
+ Stringmap home_provider;
+ if (!operator_info->sid().empty()) {
+ home_provider[kOperatorCodeKey] = operator_info->sid();
+ }
+ if (!operator_info->nid().empty()) {
+ home_provider[kOperatorCodeKey] = operator_info->nid();
+ }
+ if (!operator_info->mccmnc().empty()) {
+ home_provider[kOperatorCodeKey] = operator_info->mccmnc();
+ }
+ if (!operator_info->operator_name().empty()) {
+ home_provider[kOperatorNameKey] = operator_info->operator_name();
+ }
+ if (!operator_info->country().empty()) {
+ home_provider[kOperatorCountryKey] = operator_info->country();
+ }
+ set_home_provider(home_provider);
+
+ const ScopedVector<MobileOperatorInfo::MobileAPN> &apn_list =
+ operator_info->apn_list();
+ Stringmaps apn_list_dict;
+
+ for (const auto &mobile_apn : apn_list) {
+ Stringmap props;
+ if (!mobile_apn->apn.empty()) {
+ props[kApnProperty] = mobile_apn->apn;
+ }
+ if (!mobile_apn->username.empty()) {
+ props[kApnUsernameProperty] = mobile_apn->username;
+ }
+ if (!mobile_apn->password.empty()) {
+ props[kApnPasswordProperty] = mobile_apn->password;
+ }
+
+ // Find the first localized and non-localized name, if any.
+ if (!mobile_apn->operator_name_list.empty()) {
+ props[kApnNameProperty] = mobile_apn->operator_name_list[0].name;
+ }
+ for (const auto &lname : mobile_apn->operator_name_list) {
+ if (!lname.language.empty()) {
+ props[kApnLocalizedNameProperty] = lname.name;
+ }
+ }
+
+ apn_list_dict.push_back(props);
+ }
+ set_apn_list(apn_list_dict);
+
+ set_provider_requires_roaming(operator_info->requires_roaming());
+}
+
+void Cellular::UpdateServingOperator(
+ const MobileOperatorInfo *operator_info,
+ const MobileOperatorInfo *home_provider_info) {
+ SLOG(this, 3) << __func__;
+ if (!service()) {
+ return;
+ }
+
+ Stringmap serving_operator;
+ if (!operator_info->sid().empty()) {
+ serving_operator[kOperatorCodeKey] = operator_info->sid();
+ }
+ if (!operator_info->nid().empty()) {
+ serving_operator[kOperatorCodeKey] = operator_info->nid();
+ }
+ if (!operator_info->mccmnc().empty()) {
+ serving_operator[kOperatorCodeKey] = operator_info->mccmnc();
+ }
+ if (!operator_info->operator_name().empty()) {
+ serving_operator[kOperatorNameKey] = operator_info->operator_name();
+ }
+ if (!operator_info->country().empty()) {
+ serving_operator[kOperatorCountryKey] = operator_info->country();
+ }
+ service()->set_serving_operator(serving_operator);
+
+ // Set friendly name of service.
+ string service_name;
+ if (!operator_info->operator_name().empty()) {
+ // If roaming, try to show "<home-provider> | <serving-operator>", per 3GPP
+ // rules (TS 31.102 and annex A of 122.101).
+ if (service()->roaming_state() == kRoamingStateRoaming &&
+ home_provider_info &&
+ !home_provider_info->operator_name().empty()) {
+ service_name += home_provider_info->operator_name() + " | ";
+ }
+ service_name += operator_info->operator_name();
+ } else if (!operator_info->mccmnc().empty()) {
+ // We could not get a name for the operator, just use the code.
+ service_name = "cellular_" + operator_info->mccmnc();
+ } else {
+ // We do not have any information, so must fallback to default service name.
+ // Only assign a new default name if the service doesn't already have one,
+ // because we we generate a new name each time.
+ service_name = service()->friendly_name();
+ if (!IsDefaultFriendlyServiceName(service_name)) {
+ service_name = CreateDefaultFriendlyServiceName();
+ }
+ }
+ service()->SetFriendlyName(service_name);
+}
+
+// /////////////////////////////////////////////////////////////////////////////
+// MobileOperatorInfoObserver implementation.
+Cellular::MobileOperatorInfoObserver::MobileOperatorInfoObserver(
+ Cellular *cellular)
+ : cellular_(cellular),
+ capability_(nullptr) {}
+
+Cellular::MobileOperatorInfoObserver::~MobileOperatorInfoObserver() {}
+
+void Cellular::MobileOperatorInfoObserver::OnOperatorChanged() {
+ SLOG(cellular_, 3) << __func__;
+
+ // Give the capabilities a chance to hook in and update their state.
+ // Some tests set |capability_| to nullptr avoid having to expect the full
+ // behaviour caused by this call.
+ if (capability_) {
+ capability_->OnOperatorChanged();
+ }
+
+ const MobileOperatorInfo *home_provider_info =
+ cellular_->home_provider_info();
+ const MobileOperatorInfo *serving_operator_info =
+ cellular_->serving_operator_info();
+
+ const bool home_provider_known =
+ home_provider_info->IsMobileNetworkOperatorKnown();
+ const bool serving_operator_known =
+ serving_operator_info->IsMobileNetworkOperatorKnown();
+
+ if (home_provider_known) {
+ cellular_->UpdateHomeProvider(home_provider_info);
+ } else if (serving_operator_known) {
+ SLOG(cellular_, 2) << "Serving provider proxying in for home provider.";
+ cellular_->UpdateHomeProvider(serving_operator_info);
+ }
+
+ if (serving_operator_known) {
+ if (home_provider_known) {
+ cellular_->UpdateServingOperator(serving_operator_info,
+ home_provider_info);
+ } else {
+ cellular_->UpdateServingOperator(serving_operator_info, nullptr);
+ }
+ } else if (home_provider_known) {
+ cellular_->UpdateServingOperator(home_provider_info, home_provider_info);
+ }
+}
+
+} // namespace shill
diff --git a/cellular/cellular.h b/cellular/cellular.h
new file mode 100644
index 0000000..4b27add
--- /dev/null
+++ b/cellular/cellular.h
@@ -0,0 +1,570 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_H_
+#define SHILL_CELLULAR_CELLULAR_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/cellular/mobile_operator_info.h"
+#include "shill/cellular/modem_info.h"
+#include "shill/cellular/modem_proxy_interface.h"
+#include "shill/dbus_properties.h"
+#include "shill/device.h"
+#include "shill/event_dispatcher.h"
+#include "shill/metrics.h"
+#include "shill/refptr_types.h"
+#include "shill/rpc_task.h"
+
+namespace shill {
+
+class CellularCapability;
+class Error;
+class ExternalTask;
+class MobileOperatorInfo;
+class PPPDeviceFactory;
+class ProxyFactory;
+
+class Cellular : public Device, public RPCTaskDelegate {
+ public:
+ enum Type {
+ kTypeGSM,
+ kTypeCDMA,
+ kTypeUniversal, // ModemManager1
+ kTypeUniversalCDMA,
+ kTypeInvalid,
+ };
+
+ // The device states progress linearly from Disabled to Linked.
+ enum State {
+ // This is the initial state of the modem and indicates that the modem radio
+ // is not turned on.
+ kStateDisabled,
+ // This state indicates that the modem radio is turned on, and it should be
+ // possible to measure signal strength.
+ kStateEnabled,
+ // The modem has registered with a network and has signal quality
+ // measurements. A cellular service object is created.
+ kStateRegistered,
+ // The modem has connected to a network.
+ kStateConnected,
+ // The network interface is UP.
+ kStateLinked,
+ };
+
+ // This enum must be kept in sync with ModemManager's MMModemState enum.
+ enum ModemState {
+ kModemStateFailed = -1,
+ kModemStateUnknown = 0,
+ kModemStateInitializing = 1,
+ kModemStateLocked = 2,
+ kModemStateDisabled = 3,
+ kModemStateDisabling = 4,
+ kModemStateEnabling = 5,
+ kModemStateEnabled = 6,
+ kModemStateSearching = 7,
+ kModemStateRegistered = 8,
+ kModemStateDisconnecting = 9,
+ kModemStateConnecting = 10,
+ kModemStateConnected = 11,
+ };
+
+ // |owner| is the ModemManager DBus service owner (e.g., ":1.17").
+ // |path| is the ModemManager.Modem DBus object path (e.g.,
+ // "/org/chromium/ModemManager/Gobi/0").
+ // |service| is the modem mananager service name (e.g.,
+ // /org/chromium/ModemManager or /org/freedesktop/ModemManager1).
+ Cellular(ModemInfo *modem_info,
+ const std::string &link_name,
+ const std::string &address,
+ int interface_index,
+ Type type,
+ const std::string &owner,
+ const std::string &service,
+ const std::string &path,
+ ProxyFactory *proxy_factory);
+ ~Cellular() override;
+
+ // Load configuration for the device from |storage|.
+ virtual bool Load(StoreInterface *storage);
+
+ // Save configuration for the device to |storage|.
+ virtual bool Save(StoreInterface *storage);
+
+ // Asynchronously connects the modem to the network. Populates |error| on
+ // failure, leaves it unchanged otherwise.
+ virtual void Connect(Error *error);
+
+ // Asynchronously disconnects the modem from the network and populates
+ // |error| on failure, leaves it unchanged otherwise.
+ virtual void Disconnect(Error *error, const char *reason);
+
+ // Asynchronously activates the modem. Returns an error on failure.
+ void Activate(const std::string &carrier, Error *error,
+ const ResultCallback &callback);
+
+ // Performs the necessary steps to bring the service to the activated state,
+ // once an online payment has been done.
+ void CompleteActivation(Error *error);
+
+ const CellularServiceRefPtr &service() const { return service_; }
+ MobileOperatorInfo *home_provider_info() const {
+ return home_provider_info_.get();
+ }
+ MobileOperatorInfo *serving_operator_info() const {
+ return serving_operator_info_.get();
+ }
+
+ // Deregisters and destructs the current service and destroys the connection,
+ // if any. This also eliminates the circular references between this device
+ // and the associated service, allowing eventual device destruction.
+ virtual void DestroyService();
+
+ static std::string GetStateString(State state);
+ static std::string GetModemStateString(ModemState modem_state);
+
+ std::string CreateDefaultFriendlyServiceName();
+ bool IsDefaultFriendlyServiceName(const std::string &service_name) const;
+
+ // Update the home provider from the information in |operator_info|. This
+ // information may be from the SIM / received OTA.
+ void UpdateHomeProvider(const MobileOperatorInfo *operator_info);
+ // Update the serving operator using information in |operator_info|.
+ // Additionally, if |home_provider_info| is not nullptr, use it to come up
+ // with a better name.
+ void UpdateServingOperator(const MobileOperatorInfo *operator_info,
+ const MobileOperatorInfo *home_provider_info);
+
+ State state() const { return state_; }
+
+ void set_modem_state(ModemState state) { modem_state_ = state; }
+ ModemState modem_state() const { return modem_state_; }
+ bool IsUnderlyingDeviceEnabled() const;
+ bool IsModemRegistered() const;
+ static bool IsEnabledModemState(ModemState state);
+
+ void HandleNewSignalQuality(uint32_t strength);
+
+ // Processes a change in the modem registration state, possibly creating,
+ // destroying or updating the CellularService.
+ void HandleNewRegistrationState();
+
+ virtual void OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties);
+
+ // Inherited from Device.
+ void Start(Error *error,
+ const EnabledStateChangedCallback &callback) override;
+ void Stop(Error *error, const EnabledStateChangedCallback &callback) override;
+ void LinkEvent(unsigned int flags, unsigned int change) override;
+ void Scan(ScanType /*scan_type*/, Error *error,
+ const std::string &/*reason*/) override;
+ void RegisterOnNetwork(const std::string &network_id,
+ Error *error,
+ const ResultCallback &callback) override;
+ void RequirePIN(const std::string &pin, bool require,
+ Error *error, const ResultCallback &callback) override;
+ void EnterPIN(const std::string &pin,
+ Error *error, const ResultCallback &callback) override;
+ void UnblockPIN(const std::string &unblock_code,
+ const std::string &pin,
+ Error *error, const ResultCallback &callback) override;
+ void ChangePIN(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error, const ResultCallback &callback) override;
+ void Reset(Error *error, const ResultCallback &callback) override;
+ void SetCarrier(const std::string &carrier,
+ Error *error, const ResultCallback &callback) override;
+ bool IsIPv6Allowed() const override;
+ void DropConnection() override;
+ void SetServiceState(Service::ConnectState state) override;
+ void SetServiceFailure(Service::ConnectFailure failure_state) override;
+ void SetServiceFailureSilent(Service::ConnectFailure failure_state) override;
+ void OnBeforeSuspend(const ResultCallback &callback) override;
+ void OnAfterResume() override;
+
+ void StartModemCallback(const EnabledStateChangedCallback &callback,
+ const Error &error);
+ void StopModemCallback(const EnabledStateChangedCallback &callback,
+ const Error &error);
+ void OnDisabled();
+ void OnEnabled();
+ void OnConnecting();
+ void OnConnected();
+ void OnConnectFailed(const Error &error);
+ void OnDisconnected();
+ void OnDisconnectFailed();
+ std::string GetTechnologyFamily(Error *error);
+ void OnModemStateChanged(ModemState new_state);
+ void OnScanReply(const Stringmaps &found_networks, const Error &error);
+
+ // accessor to read the allow roaming property
+ bool allow_roaming_property() const { return allow_roaming_; }
+ // Is the underlying device in the process of activating?
+ bool IsActivating() const;
+
+ // Initiate PPP link. Called from capabilities.
+ virtual void StartPPP(const std::string &serial_device);
+ // Callback for |ppp_task_|.
+ virtual void OnPPPDied(pid_t pid, int exit);
+ // Implements RPCTaskDelegate, for |ppp_task_|.
+ void GetLogin(std::string *user, std::string *password) override;
+ void Notify(const std::string &reason,
+ const std::map<std::string, std::string> &dict) override;
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // DBus Properties exposed by the Device interface of shill.
+ void RegisterProperties();
+
+ // getters
+ const std::string &dbus_owner() const { return dbus_owner_; }
+ const std::string &dbus_service() const { return dbus_service_; }
+ const std::string &dbus_path() const { return dbus_path_; }
+ const Stringmap &home_provider() const { return home_provider_; }
+ const std::string &carrier() const { return carrier_; }
+ bool scanning_supported() const { return scanning_supported_; }
+ const std::string &esn() const { return esn_; }
+ const std::string &firmware_revision() const { return firmware_revision_; }
+ const std::string &hardware_revision() const { return hardware_revision_; }
+ const std::string &imei() const { return imei_; }
+ const std::string &imsi() const { return imsi_; }
+ const std::string &mdn() const { return mdn_; }
+ const std::string &meid() const { return meid_; }
+ const std::string &min() const { return min_; }
+ const std::string &manufacturer() const { return manufacturer_; }
+ const std::string &model_id() const { return model_id_; }
+ const std::string &mm_plugin() const { return mm_plugin_; }
+ bool scanning() const { return scanning_; }
+
+ const std::string &selected_network() const { return selected_network_; }
+ const Stringmaps &found_networks() const { return found_networks_; }
+ bool provider_requires_roaming() const { return provider_requires_roaming_; }
+ bool sim_present() const { return sim_present_; }
+ const Stringmaps &apn_list() const { return apn_list_; }
+ const std::string &sim_identifier() const { return sim_identifier_; }
+
+ const Strings &supported_carriers() const { return supported_carriers_; }
+ uint16_t prl_version() const { return prl_version_; }
+
+ // setters
+ void set_home_provider(const Stringmap &home_provider);
+ void set_carrier(const std::string &carrier);
+ void set_scanning_supported(bool scanning_supported);
+ void set_esn(const std::string &esn);
+ void set_firmware_revision(const std::string &firmware_revision);
+ void set_hardware_revision(const std::string &hardware_revision);
+ void set_imei(const std::string &imei);
+ void set_imsi(const std::string &imsi);
+ void set_mdn(const std::string &mdn);
+ void set_meid(const std::string &meid);
+ void set_min(const std::string &min);
+ void set_manufacturer(const std::string &manufacturer);
+ void set_model_id(const std::string &model_id);
+ void set_mm_plugin(const std::string &mm_plugin);
+ void set_scanning(bool scanning);
+
+ void set_selected_network(const std::string &selected_network);
+ void clear_found_networks();
+ void set_found_networks(const Stringmaps &found_networks);
+ void set_provider_requires_roaming(bool provider_requires_roaming);
+ void set_sim_present(bool sim_present);
+ void set_apn_list(const Stringmaps &apn_list);
+ void set_sim_identifier(const std::string &sim_identifier);
+
+ void set_supported_carriers(const Strings &supported_carriers);
+ void set_prl_version(uint16_t prl_version);
+
+ // Takes ownership.
+ void set_home_provider_info(MobileOperatorInfo *home_provider_info);
+ // Takes ownership.
+ void set_serving_operator_info(MobileOperatorInfo *serving_operator_info);
+
+ private:
+ friend class ActivePassiveOutOfCreditsDetectorTest;
+ friend class CellularTest;
+ friend class CellularCapabilityTest;
+ friend class CellularCapabilityCDMATest;
+ friend class CellularCapabilityGSMTest;
+ friend class CellularCapabilityUniversalTest;
+ friend class CellularCapabilityUniversalCDMATest;
+ friend class CellularServiceTest;
+ friend class ModemTest;
+ friend class SubscriptionStateOutOfCreditsDetectorTest;
+ FRIEND_TEST(CellularCapabilityCDMATest, GetRegistrationState);
+ FRIEND_TEST(CellularCapabilityGSMTest, AllowRoaming);
+ FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
+ FRIEND_TEST(CellularCapabilityTest, EnableModemFail);
+ FRIEND_TEST(CellularCapabilityTest, EnableModemSucceed);
+ FRIEND_TEST(CellularCapabilityTest, FinishEnable);
+ FRIEND_TEST(CellularCapabilityTest, GetModemInfo);
+ FRIEND_TEST(CellularCapabilityTest, GetModemStatus);
+ FRIEND_TEST(CellularCapabilityUniversalCDMATest, OnCDMARegistrationChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, AllowRoaming);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, Connect);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, IsServiceActivationRequired);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StartModemAlreadyEnabled);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StopModemConnected);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdatePendingActivationState);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdateRegistrationState);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdateRegistrationStateModemNotConnected);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateScanningProperty);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdateServiceActivationState);
+ FRIEND_TEST(CellularTest, ChangeServiceState);
+ FRIEND_TEST(CellularTest, ChangeServiceStatePPP);
+ FRIEND_TEST(CellularTest, CreateService);
+ FRIEND_TEST(CellularTest, Connect);
+ FRIEND_TEST(CellularTest, ConnectFailure);
+ FRIEND_TEST(CellularTest, ConnectFailureNoService);
+ FRIEND_TEST(CellularTest, ConnectSuccessNoService);
+ FRIEND_TEST(CellularTest, CustomSetterNoopChange);
+ FRIEND_TEST(CellularTest, DisableModem);
+ FRIEND_TEST(CellularTest, Disconnect);
+ FRIEND_TEST(CellularTest, DisconnectFailure);
+ FRIEND_TEST(CellularTest, DisconnectWithCallback);
+ FRIEND_TEST(CellularTest, DropConnection);
+ FRIEND_TEST(CellularTest, DropConnectionPPP);
+ FRIEND_TEST(CellularTest, EnableTrafficMonitor);
+ FRIEND_TEST(CellularTest, EstablishLinkDHCP);
+ FRIEND_TEST(CellularTest, EstablishLinkPPP);
+ FRIEND_TEST(CellularTest, EstablishLinkStatic);
+ FRIEND_TEST(CellularTest, FriendlyServiceName);
+ FRIEND_TEST(CellularTest,
+ HandleNewRegistrationStateForServiceRequiringActivation);
+ FRIEND_TEST(CellularTest, HomeProviderServingOperator);
+ FRIEND_TEST(CellularTest, LinkEventUpWithPPP);
+ FRIEND_TEST(CellularTest, LinkEventUpWithoutPPP);
+ FRIEND_TEST(CellularTest, LinkEventWontDestroyService);
+ FRIEND_TEST(CellularTest, ModemStateChangeDisable);
+ FRIEND_TEST(CellularTest, ModemStateChangeEnable);
+ FRIEND_TEST(CellularTest, ModemStateChangeStaleConnected);
+ FRIEND_TEST(CellularTest, ModemStateChangeValidConnected);
+ FRIEND_TEST(CellularTest, Notify);
+ FRIEND_TEST(CellularTest, OnAfterResumeDisableInProgressWantDisabled);
+ FRIEND_TEST(CellularTest, OnAfterResumeDisableQueuedWantEnabled);
+ FRIEND_TEST(CellularTest, OnAfterResumeDisabledWantDisabled);
+ FRIEND_TEST(CellularTest, OnAfterResumeDisabledWantEnabled);
+ FRIEND_TEST(CellularTest, OnAfterResumePowerDownInProgressWantEnabled);
+ FRIEND_TEST(CellularTest, OnConnectionHealthCheckerResult);
+ FRIEND_TEST(CellularTest, OnPPPDied);
+ FRIEND_TEST(CellularTest, PPPConnectionFailedAfterAuth);
+ FRIEND_TEST(CellularTest, PPPConnectionFailedBeforeAuth);
+ FRIEND_TEST(CellularTest, PPPConnectionFailedDuringAuth);
+ FRIEND_TEST(CellularTest, ScanAsynchronousFailure);
+ FRIEND_TEST(CellularTest, ScanImmediateFailure);
+ FRIEND_TEST(CellularTest, ScanSuccess);
+ FRIEND_TEST(CellularTest, SetAllowRoaming);
+ FRIEND_TEST(CellularTest, StartModemCallback);
+ FRIEND_TEST(CellularTest, StartModemCallbackFail);
+ FRIEND_TEST(CellularTest, StopModemCallback);
+ FRIEND_TEST(CellularTest, StopModemCallbackFail);
+ FRIEND_TEST(CellularTest, StopPPPOnDisconnect);
+ FRIEND_TEST(CellularTest, StopPPPOnTermination);
+ FRIEND_TEST(CellularTest, StorageIdentifier);
+ FRIEND_TEST(CellularTest, StartConnected);
+ FRIEND_TEST(CellularTest, StartCDMARegister);
+ FRIEND_TEST(CellularTest, StartGSMRegister);
+ FRIEND_TEST(CellularTest, StartLinked);
+ FRIEND_TEST(CellularTest, StartPPP);
+ FRIEND_TEST(CellularTest, StartPPPAfterEthernetUp);
+ FRIEND_TEST(CellularTest, StartPPPAlreadyStarted);
+ FRIEND_TEST(CellularTest, UpdateScanning);
+ FRIEND_TEST(Modem1Test, CreateDeviceMM1);
+
+ class MobileOperatorInfoObserver : public MobileOperatorInfo::Observer {
+ public:
+ // |cellular| must have lifespan longer than this object. In practice this
+ // is enforced because |cellular| owns this object.
+ explicit MobileOperatorInfoObserver(Cellular *cellular);
+ ~MobileOperatorInfoObserver() override;
+
+ void set_capability(CellularCapability *capability) {
+ capability_ = capability;
+ }
+
+ // Inherited from MobileOperatorInfo::Observer
+ void OnOperatorChanged() override;
+
+ private:
+ Cellular *const cellular_;
+ // Owned by |Cellular|.
+ CellularCapability *capability_;
+
+ DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoObserver);
+ };
+
+ // Names of properties in storage
+ static const char kAllowRoaming[];
+
+ // the |kScanningProperty| exposed by Cellular device is sticky false. Every
+ // time it is set to true, it must be reset to false after a time equal to
+ // this constant.
+ static const int64_t kDefaultScanningTimeoutMilliseconds;
+
+ // Generic service name prefix, shown when the correct carrier name is
+ // unknown.
+ static const char kGenericServiceNamePrefix[];
+ static unsigned int friendly_service_name_id_;
+
+ void SetState(State state);
+
+ // Invoked when the modem is connected to the cellular network to transition
+ // to the network-connected state and bring the network interface up.
+ void EstablishLink();
+
+ void InitCapability(Type type);
+
+ void CreateService();
+
+ // HelpRegisterDerived*: Expose a property over RPC, with the name |name|.
+ //
+ // Reads of the property will be handled by invoking |get|.
+ // Writes to the property will be handled by invoking |set|.
+ // Clearing the property will be handled by PropertyStore.
+ void HelpRegisterDerivedBool(
+ const std::string &name,
+ bool(Cellular::*get)(Error *error),
+ bool(Cellular::*set)(const bool &value, Error *error));
+ void HelpRegisterConstDerivedString(
+ const std::string &name,
+ std::string(Cellular::*get)(Error *error));
+
+ void OnConnectReply(const Error &error);
+ void OnDisconnectReply(const Error &error);
+
+ // DBUS accessors to read/modify the allow roaming property
+ bool GetAllowRoaming(Error */*error*/) { return allow_roaming_; }
+ bool SetAllowRoaming(const bool &value, Error *error);
+
+ // When shill terminates or ChromeOS suspends, this function is called to
+ // disconnect from the cellular network.
+ void StartTermination();
+
+ // This method is invoked upon the completion of StartTermination().
+ void OnTerminationCompleted(const Error &error);
+
+ // This function does the final cleanup once a disconnect request terminates.
+ // Returns true, if the device state is successfully changed.
+ bool DisconnectCleanup();
+
+ // Executed after the asynchronous CellularCapability::StartModem
+ // call from OnAfterResume completes.
+ static void LogRestartModemResult(const Error &error);
+
+ // Terminate the pppd process associated with this Device, and remove the
+ // association between the PPPDevice and our CellularService. If this
+ // Device is not using PPP, the method has no effect.
+ void StopPPP();
+
+ // Handlers for PPP events. Dispatched from Notify().
+ void OnPPPAuthenticated();
+ void OnPPPAuthenticating();
+ void OnPPPConnected(const std::map<std::string, std::string> ¶ms);
+ void OnPPPDisconnected();
+
+ void UpdateScanning();
+
+ base::WeakPtrFactory<Cellular> weak_ptr_factory_;
+
+ State state_;
+ ModemState modem_state_;
+
+ std::unique_ptr<CellularCapability> capability_;
+
+ // Operator info objects. These objects receive updates as we receive
+ // information about the network operators from the SIM or OTA. In turn, they
+ // send out updates through their observer interfaces whenever the identity of
+ // the network operator changes, or any other property of the operator
+ // changes.
+ std::unique_ptr<MobileOperatorInfo> home_provider_info_;
+ std::unique_ptr<MobileOperatorInfo> serving_operator_info_;
+ // Observer object to listen to updates from the operator info objects.
+ std::unique_ptr<MobileOperatorInfoObserver> mobile_operator_info_observer_;
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // All DBus Properties exposed by the Cellular device.
+ // Properties common to GSM and CDMA modems.
+ const std::string dbus_owner_; // :x.y
+ const std::string dbus_service_; // org.*.ModemManager*
+ const std::string dbus_path_; // ModemManager.Modem
+ Stringmap home_provider_;
+
+ bool scanning_supported_;
+ std::string carrier_;
+ std::string esn_;
+ std::string firmware_revision_;
+ std::string hardware_revision_;
+ std::string imei_;
+ std::string imsi_;
+ std::string manufacturer_;
+ std::string mdn_;
+ std::string meid_;
+ std::string min_;
+ std::string model_id_;
+ std::string mm_plugin_;
+ bool scanning_;
+
+ // GSM only properties.
+ // They are always exposed but are non empty only for GSM technology modems.
+ std::string selected_network_;
+ Stringmaps found_networks_;
+ bool provider_requires_roaming_;
+ uint16_t scan_interval_;
+ bool sim_present_;
+ Stringmaps apn_list_;
+ std::string sim_identifier_;
+
+ // CDMA only properties.
+ uint16_t prl_version_;
+
+ // This property is specific to Gobi modems.
+ Strings supported_carriers_;
+ // End of DBus properties.
+ // ///////////////////////////////////////////////////////////////////////////
+
+ ModemInfo *modem_info_;
+ const Type type_;
+ ProxyFactory *proxy_factory_;
+ PPPDeviceFactory *ppp_device_factory_;
+
+ CellularServiceRefPtr service_;
+
+ // User preference to allow or disallow roaming
+ bool allow_roaming_;
+
+ // Track whether a user initiated scan is in prgoress (initiated via ::Scan)
+ bool proposed_scan_in_progress_;
+
+ // Flag indicating that a disconnect has been explicitly requested.
+ bool explicit_disconnect_;
+
+ std::unique_ptr<ExternalTask> ppp_task_;
+ PPPDeviceRefPtr ppp_device_;
+ bool is_ppp_authenticating_;
+
+ // Sometimes modems may be stuck in the SEARCHING state during the lack of
+ // presence of a network. During this indefinite duration of time, keeping
+ // the Device.Scanning property as |true| causes a bad user experience.
+ // This callback sets it to |false| after a timeout period has passed.
+ base::CancelableClosure scanning_timeout_callback_;
+ int64_t scanning_timeout_milliseconds_;
+
+ DISALLOW_COPY_AND_ASSIGN(Cellular);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_H_
diff --git a/cellular/cellular_bearer.cc b/cellular/cellular_bearer.cc
new file mode 100644
index 0000000..0e9ba44
--- /dev/null
+++ b/cellular/cellular_bearer.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2014 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/cellular_bearer.h"
+
+#include <ModemManager/ModemManager.h>
+
+#include <base/bind.h>
+
+#include "shill/dbus_properties.h"
+#include "shill/dbus_properties_proxy.h"
+#include "shill/logging.h"
+#include "shill/proxy_factory.h"
+
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(const CellularBearer *c) { return "(cellular_bearer)"; }
+}
+
+namespace {
+
+const char kPropertyAddress[] = "address";
+const char kPropertyDNS1[] = "dns1";
+const char kPropertyDNS2[] = "dns2";
+const char kPropertyDNS3[] = "dns3";
+const char kPropertyGateway[] = "gateway";
+const char kPropertyMethod[] = "method";
+const char kPropertyPrefix[] = "prefix";
+
+IPConfig::Method ConvertMMBearerIPConfigMethod(uint32_t method) {
+ switch (method) {
+ case MM_BEARER_IP_METHOD_PPP:
+ return IPConfig::kMethodPPP;
+ case MM_BEARER_IP_METHOD_STATIC:
+ return IPConfig::kMethodStatic;
+ case MM_BEARER_IP_METHOD_DHCP:
+ return IPConfig::kMethodDHCP;
+ default:
+ return IPConfig::kMethodUnknown;
+ }
+}
+
+} // namespace
+
+CellularBearer::CellularBearer(ProxyFactory *proxy_factory,
+ const string &dbus_path,
+ const string &dbus_service)
+ : proxy_factory_(proxy_factory),
+ dbus_path_(dbus_path),
+ dbus_service_(dbus_service),
+ connected_(false),
+ ipv4_config_method_(IPConfig::kMethodUnknown),
+ ipv6_config_method_(IPConfig::kMethodUnknown) {
+ CHECK(proxy_factory_);
+}
+
+CellularBearer::~CellularBearer() {}
+
+bool CellularBearer::Init() {
+ SLOG(this, 3) << __func__ << ": path='" << dbus_path_
+ << "', service='" << dbus_service_ << "'";
+
+ dbus_properties_proxy_.reset(
+ proxy_factory_->CreateDBusPropertiesProxy(dbus_path_, dbus_service_));
+ // It is possible that ProxyFactory::CreateDBusPropertiesProxy() returns
+ // nullptr as the bearer DBus object may no longer exist.
+ if (!dbus_properties_proxy_) {
+ LOG(WARNING) << "Failed to create DBus properties proxy for bearer '"
+ << dbus_path_ << "'. Bearer is likely gone.";
+ return false;
+ }
+
+ dbus_properties_proxy_->set_properties_changed_callback(base::Bind(
+ &CellularBearer::OnDBusPropertiesChanged, base::Unretained(this)));
+ UpdateProperties();
+ return true;
+}
+
+void CellularBearer::GetIPConfigMethodAndProperties(
+ const DBusPropertiesMap &properties,
+ IPAddress::Family address_family,
+ IPConfig::Method *ipconfig_method,
+ std::unique_ptr<IPConfig::Properties> *ipconfig_properties) const {
+ DCHECK(ipconfig_method);
+ DCHECK(ipconfig_properties);
+
+ uint32_t method;
+ if (!DBusProperties::GetUint32(properties, kPropertyMethod, &method)) {
+ SLOG(this, 2) << "Bearer '" << dbus_path_
+ << "' does not specify an IP configuration method.";
+ method = MM_BEARER_IP_METHOD_UNKNOWN;
+ }
+ *ipconfig_method = ConvertMMBearerIPConfigMethod(method);
+ ipconfig_properties->reset();
+
+ if (*ipconfig_method != IPConfig::kMethodStatic)
+ return;
+
+ string address, gateway;
+ if (!DBusProperties::GetString(properties, kPropertyAddress, &address) ||
+ !DBusProperties::GetString(properties, kPropertyGateway, &gateway)) {
+ SLOG(this, 2) << "Bearer '" << dbus_path_
+ << "' static IP configuration does not specify valid "
+ "address/gateway information.";
+ *ipconfig_method = IPConfig::kMethodUnknown;
+ return;
+ }
+
+ ipconfig_properties->reset(new IPConfig::Properties);
+ (*ipconfig_properties)->address_family = address_family;
+ (*ipconfig_properties)->address = address;
+ (*ipconfig_properties)->gateway = gateway;
+
+ uint32_t prefix;
+ if (!DBusProperties::GetUint32(properties, kPropertyPrefix, &prefix)) {
+ prefix = IPAddress::GetMaxPrefixLength(address_family);
+ }
+ (*ipconfig_properties)->subnet_prefix = prefix;
+
+ string dns;
+ if (DBusProperties::GetString(properties, kPropertyDNS1, &dns)) {
+ (*ipconfig_properties)->dns_servers.push_back(dns);
+ }
+ if (DBusProperties::GetString(properties, kPropertyDNS2, &dns)) {
+ (*ipconfig_properties)->dns_servers.push_back(dns);
+ }
+ if (DBusProperties::GetString(properties, kPropertyDNS3, &dns)) {
+ (*ipconfig_properties)->dns_servers.push_back(dns);
+ }
+}
+
+void CellularBearer::ResetProperties() {
+ connected_ = false;
+ data_interface_.clear();
+ ipv4_config_method_ = IPConfig::kMethodUnknown;
+ ipv4_config_properties_.reset();
+ ipv6_config_method_ = IPConfig::kMethodUnknown;
+ ipv6_config_properties_.reset();
+}
+
+void CellularBearer::UpdateProperties() {
+ ResetProperties();
+
+ if (!dbus_properties_proxy_)
+ return;
+
+ DBusPropertiesMap properties =
+ dbus_properties_proxy_->GetAll(MM_DBUS_INTERFACE_BEARER);
+ if (properties.empty()) {
+ LOG(WARNING) << "Could not get properties of bearer '" << dbus_path_
+ << "'. Bearer is likely gone and thus ignored.";
+ return;
+ }
+
+ OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+}
+
+void CellularBearer::OnDBusPropertiesChanged(
+ const string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const vector<string> &/*invalidated_properties*/) {
+ SLOG(this, 3) << __func__ << ": path=" << dbus_path_
+ << ", interface=" << interface;
+
+ if (interface != MM_DBUS_INTERFACE_BEARER)
+ return;
+
+ bool connected;
+ if (DBusProperties::GetBool(
+ changed_properties, MM_BEARER_PROPERTY_CONNECTED, &connected)) {
+ connected_ = connected;
+ }
+
+ string data_interface;
+ if (DBusProperties::GetString(
+ changed_properties, MM_BEARER_PROPERTY_INTERFACE, &data_interface)) {
+ data_interface_ = data_interface;
+ }
+
+ DBusPropertiesMap ipconfig;
+ if (DBusProperties::GetDBusPropertiesMap(
+ changed_properties, MM_BEARER_PROPERTY_IP4CONFIG, &ipconfig)) {
+ GetIPConfigMethodAndProperties(ipconfig,
+ IPAddress::kFamilyIPv4,
+ &ipv4_config_method_,
+ &ipv4_config_properties_);
+ }
+ if (DBusProperties::GetDBusPropertiesMap(
+ changed_properties, MM_BEARER_PROPERTY_IP6CONFIG, &ipconfig)) {
+ GetIPConfigMethodAndProperties(ipconfig,
+ IPAddress::kFamilyIPv6,
+ &ipv6_config_method_,
+ &ipv6_config_properties_);
+ }
+}
+
+} // namespace shill
diff --git a/cellular/cellular_bearer.h b/cellular/cellular_bearer.h
new file mode 100644
index 0000000..b95a8c8
--- /dev/null
+++ b/cellular/cellular_bearer.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2014 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_BEARER_H_
+#define SHILL_CELLULAR_CELLULAR_BEARER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>
+
+#include "shill/dbus_properties.h"
+#include "shill/ipconfig.h"
+
+namespace shill {
+
+class DBusPropertiesProxyInterface;
+class ProxyFactory;
+
+// A class for observing property changes of a bearer object exposed by
+// ModemManager.
+class CellularBearer {
+ public:
+ // Constructs a cellular bearer for observing property changes of a
+ // corresponding bearer object, at the DBus path |dbus_path| of DBus service
+ // |dbus_service|, exposed by ModemManager. The ownership of |proxy_factory|
+ // is not transferred, and should outlive this object.
+ //
+ // TODO(benchan): Use a context object approach to pass objects like
+ // ProxyFactory through constructor.
+ CellularBearer(ProxyFactory *proxy_factory,
+ const std::string &dbus_path,
+ const std::string &dbus_service);
+ ~CellularBearer();
+
+ // Initializes this object by creating a DBus properties proxy to observe
+ // property changes of the corresponding bearer object exposed by ModemManager
+ // and also fetching the current properties of the bearer. Returns true on
+ // success or false if it fails to the DBus properties proxy.
+ bool Init();
+
+ // Callback upon DBus property changes of the bearer.
+ void OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties);
+
+ const std::string &dbus_path() const { return dbus_path_; }
+ const std::string &dbus_service() const { return dbus_service_; }
+
+ bool connected() const { return connected_; }
+ const std::string &data_interface() const { return data_interface_; }
+ IPConfig::Method ipv4_config_method() const { return ipv4_config_method_; }
+ const IPConfig::Properties *ipv4_config_properties() const {
+ return ipv4_config_properties_.get();
+ }
+ IPConfig::Method ipv6_config_method() const { return ipv6_config_method_; }
+ const IPConfig::Properties *ipv6_config_properties() const {
+ return ipv6_config_properties_.get();
+ }
+
+ private:
+ friend class CellularBearerTest;
+ FRIEND_TEST(CellularTest, EstablishLinkDHCP);
+ FRIEND_TEST(CellularTest, EstablishLinkPPP);
+ FRIEND_TEST(CellularTest, EstablishLinkStatic);
+
+ // Gets the IP configuration method and properties from |properties|.
+ // |address_family| specifies the IP address family of the configuration.
+ // |ipconfig_method| and |ipconfig_properties| are used to return the IP
+ // configuration method and properties and should be non-NULL.
+ void GetIPConfigMethodAndProperties(
+ const DBusPropertiesMap &properties,
+ IPAddress::Family address_family,
+ IPConfig::Method *ipconfig_method,
+ std::unique_ptr<IPConfig::Properties> *ipconfig_properties) const;
+
+ // Resets bearer properties.
+ void ResetProperties();
+
+ // Updates bearer properties by fetching the current properties of the
+ // corresponding bearer object exposed by ModemManager over DBus.
+ void UpdateProperties();
+
+ // Setters for unit tests.
+ void set_connected(bool connected) { connected_ = connected; }
+ void set_data_interface(const std::string &data_interface) {
+ data_interface_ = data_interface;
+ }
+ void set_ipv4_config_method(IPConfig::Method ipv4_config_method) {
+ ipv4_config_method_ = ipv4_config_method;
+ }
+ void set_ipv4_config_properties(
+ std::unique_ptr<IPConfig::Properties> ipv4_config_properties) {
+ ipv4_config_properties_ = std::move(ipv4_config_properties);
+ }
+ void set_ipv6_config_method(IPConfig::Method ipv6_config_method) {
+ ipv6_config_method_ = ipv6_config_method;
+ }
+ void set_ipv6_config_properties(
+ std::unique_ptr<IPConfig::Properties> ipv6_config_properties) {
+ ipv6_config_properties_ = std::move(ipv6_config_properties);
+ }
+
+ ProxyFactory *proxy_factory_;
+ std::string dbus_path_;
+ std::string dbus_service_;
+ std::unique_ptr<DBusPropertiesProxyInterface> dbus_properties_proxy_;
+ bool connected_;
+ std::string data_interface_;
+
+ // If |ipv4_config_method_| is set to |IPConfig::kMethodStatic|,
+ // |ipv4_config_properties_| is guaranteed to contain valid IP configuration
+ // properties. Otherwise, |ipv4_config_properties_| is set to nullptr.
+ // |ipv6_config_properties_| is handled similarly.
+ IPConfig::Method ipv4_config_method_;
+ std::unique_ptr<IPConfig::Properties> ipv4_config_properties_;
+ IPConfig::Method ipv6_config_method_;
+ std::unique_ptr<IPConfig::Properties> ipv6_config_properties_;
+
+ DISALLOW_COPY_AND_ASSIGN(CellularBearer);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_BEARER_H_
diff --git a/cellular/cellular_bearer_unittest.cc b/cellular/cellular_bearer_unittest.cc
new file mode 100644
index 0000000..f21bfab
--- /dev/null
+++ b/cellular/cellular_bearer_unittest.cc
@@ -0,0 +1,262 @@
+// Copyright (c) 2014 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/cellular_bearer.h"
+
+#include <ModemManager/ModemManager.h>
+
+#include "shill/mock_dbus_properties_proxy.h"
+#include "shill/mock_proxy_factory.h"
+#include "shill/testing.h"
+
+using std::string;
+using std::vector;
+using testing::Return;
+using testing::ReturnNull;
+using testing::_;
+
+namespace shill {
+
+namespace {
+
+const char kBearerDBusPath[] = "/org/freedesktop/ModemManager/Bearer/0";
+const char kBearerDBusService[] = "org.freedesktop.ModemManager";
+const char kDataInterface[] = "/dev/ppp0";
+const char kIPv4Address[] = "10.0.0.1";
+const char kIPv4Gateway[] = "10.0.0.254";
+const int kIPv4SubnetPrefix = 8;
+const char *const kIPv4DNS[] = { "10.0.0.2", "8.8.4.4", "8.8.8.8" };
+const char kIPv6Address[] = "0:0:0:0:0:ffff:a00:1";
+const char kIPv6Gateway[] = "0:0:0:0:0:ffff:a00:fe";
+const int kIPv6SubnetPrefix = 16;
+const char *const kIPv6DNS[] = {
+ "0:0:0:0:0:ffff:a00:fe", "0:0:0:0:0:ffff:808:404", "0:0:0:0:0:ffff:808:808"
+};
+
+} // namespace
+
+class CellularBearerTest : public testing::Test {
+ public:
+ CellularBearerTest()
+ : proxy_factory_(new MockProxyFactory()),
+ bearer_(proxy_factory_.get(), kBearerDBusPath, kBearerDBusService) {}
+
+ protected:
+ void VerifyDefaultProperties() {
+ EXPECT_EQ(kBearerDBusPath, bearer_.dbus_path());
+ EXPECT_EQ(kBearerDBusService, bearer_.dbus_service());
+ EXPECT_FALSE(bearer_.connected());
+ EXPECT_EQ("", bearer_.data_interface());
+ EXPECT_EQ(IPConfig::kMethodUnknown, bearer_.ipv4_config_method());
+ EXPECT_EQ(nullptr, bearer_.ipv4_config_properties());;
+ EXPECT_EQ(IPConfig::kMethodUnknown, bearer_.ipv6_config_method());
+ EXPECT_EQ(nullptr, bearer_.ipv6_config_properties());;
+ }
+
+ static DBusPropertiesMap ConstructIPv4ConfigProperties(
+ MMBearerIpMethod ipconfig_method) {
+ DBusPropertiesMap ipconfig_properties;
+ ipconfig_properties["method"].writer().append_uint32(ipconfig_method);
+ if (ipconfig_method == MM_BEARER_IP_METHOD_STATIC) {
+ ipconfig_properties["address"].writer().append_string(kIPv4Address);
+ ipconfig_properties["gateway"].writer().append_string(kIPv4Gateway);
+ ipconfig_properties["prefix"].writer().append_uint32(kIPv4SubnetPrefix);
+ ipconfig_properties["dns1"].writer().append_string(kIPv4DNS[0]);
+ ipconfig_properties["dns2"].writer().append_string(kIPv4DNS[1]);
+ ipconfig_properties["dns3"].writer().append_string(kIPv4DNS[2]);
+ }
+ return ipconfig_properties;
+ }
+
+ static DBusPropertiesMap ConstructIPv6ConfigProperties(
+ MMBearerIpMethod ipconfig_method) {
+ DBusPropertiesMap ipconfig_properties;
+ ipconfig_properties["method"].writer().append_uint32(ipconfig_method);
+ if (ipconfig_method == MM_BEARER_IP_METHOD_STATIC) {
+ ipconfig_properties["address"].writer().append_string(kIPv6Address);
+ ipconfig_properties["gateway"].writer().append_string(kIPv6Gateway);
+ ipconfig_properties["prefix"].writer().append_uint32(kIPv6SubnetPrefix);
+ ipconfig_properties["dns1"].writer().append_string(kIPv6DNS[0]);
+ ipconfig_properties["dns2"].writer().append_string(kIPv6DNS[1]);
+ ipconfig_properties["dns3"].writer().append_string(kIPv6DNS[2]);
+ }
+ return ipconfig_properties;
+ }
+
+ static DBusPropertiesMap ConstructBearerProperties(
+ bool connected, const string &data_interface,
+ MMBearerIpMethod ipv4_config_method,
+ MMBearerIpMethod ipv6_config_method) {
+ DBusPropertiesMap properties;
+ properties[MM_BEARER_PROPERTY_CONNECTED].writer().append_bool(connected);
+ properties[MM_BEARER_PROPERTY_INTERFACE].writer().append_string(
+ data_interface.c_str());
+
+ DBus::MessageIter writer;
+ writer = properties[MM_BEARER_PROPERTY_IP4CONFIG].writer();
+ writer << ConstructIPv4ConfigProperties(ipv4_config_method);
+ writer = properties[MM_BEARER_PROPERTY_IP6CONFIG].writer();
+ writer << ConstructIPv6ConfigProperties(ipv6_config_method);
+ return properties;
+ }
+
+ void VerifyStaticIPv4ConfigMethodAndProperties() {
+ EXPECT_EQ(IPConfig::kMethodStatic, bearer_.ipv4_config_method());
+ const IPConfig::Properties *ipv4_config_properties =
+ bearer_.ipv4_config_properties();
+ ASSERT_NE(nullptr, ipv4_config_properties);;
+ EXPECT_EQ(IPAddress::kFamilyIPv4, ipv4_config_properties->address_family);
+ EXPECT_EQ(kIPv4Address, ipv4_config_properties->address);
+ EXPECT_EQ(kIPv4Gateway, ipv4_config_properties->gateway);
+ EXPECT_EQ(kIPv4SubnetPrefix, ipv4_config_properties->subnet_prefix);
+ ASSERT_EQ(3, ipv4_config_properties->dns_servers.size());
+ EXPECT_EQ(kIPv4DNS[0], ipv4_config_properties->dns_servers[0]);
+ EXPECT_EQ(kIPv4DNS[1], ipv4_config_properties->dns_servers[1]);
+ EXPECT_EQ(kIPv4DNS[2], ipv4_config_properties->dns_servers[2]);
+ }
+
+ void VerifyStaticIPv6ConfigMethodAndProperties() {
+ EXPECT_EQ(IPConfig::kMethodStatic, bearer_.ipv6_config_method());
+ const IPConfig::Properties *ipv6_config_properties =
+ bearer_.ipv6_config_properties();
+ ASSERT_NE(nullptr, ipv6_config_properties);;
+ EXPECT_EQ(IPAddress::kFamilyIPv6, ipv6_config_properties->address_family);
+ EXPECT_EQ(kIPv6Address, ipv6_config_properties->address);
+ EXPECT_EQ(kIPv6Gateway, ipv6_config_properties->gateway);
+ EXPECT_EQ(kIPv6SubnetPrefix, ipv6_config_properties->subnet_prefix);
+ ASSERT_EQ(3, ipv6_config_properties->dns_servers.size());
+ EXPECT_EQ(kIPv6DNS[0], ipv6_config_properties->dns_servers[0]);
+ EXPECT_EQ(kIPv6DNS[1], ipv6_config_properties->dns_servers[1]);
+ EXPECT_EQ(kIPv6DNS[2], ipv6_config_properties->dns_servers[2]);
+ }
+
+ std::unique_ptr<MockProxyFactory> proxy_factory_;
+ CellularBearer bearer_;
+};
+
+TEST_F(CellularBearerTest, Constructor) {
+ VerifyDefaultProperties();
+}
+
+TEST_F(CellularBearerTest, Init) {
+ // Ownership of |properties_proxy| is transferred to |bearer_| via
+ // |proxy_factory_|.
+ std::unique_ptr<MockDBusPropertiesProxy> properties_proxy(
+ new MockDBusPropertiesProxy);
+ EXPECT_CALL(*proxy_factory_.get(),
+ CreateDBusPropertiesProxy(kBearerDBusPath, kBearerDBusService))
+ .WillOnce(ReturnAndReleasePointee(&properties_proxy));
+ EXPECT_CALL(*properties_proxy.get(), set_properties_changed_callback(_))
+ .Times(1);
+ EXPECT_CALL(*properties_proxy.get(), GetAll(MM_DBUS_INTERFACE_BEARER))
+ .WillOnce(Return(ConstructBearerProperties(true, kDataInterface,
+ MM_BEARER_IP_METHOD_STATIC,
+ MM_BEARER_IP_METHOD_STATIC)));
+ bearer_.Init();
+ EXPECT_TRUE(bearer_.connected());
+ EXPECT_EQ(kDataInterface, bearer_.data_interface());
+ VerifyStaticIPv4ConfigMethodAndProperties();
+ VerifyStaticIPv6ConfigMethodAndProperties();
+}
+
+TEST_F(CellularBearerTest, InitAndCreateDBusPropertiesProxyFails) {
+ EXPECT_CALL(*proxy_factory_.get(),
+ CreateDBusPropertiesProxy(kBearerDBusPath, kBearerDBusService))
+ .WillOnce(ReturnNull());
+ bearer_.Init();
+ VerifyDefaultProperties();
+}
+
+TEST_F(CellularBearerTest, OnDBusPropertiesChanged) {
+ DBusPropertiesMap properties;
+
+ // If interface is not MM_DBUS_INTERFACE_BEARER, no updates should be done.
+ bearer_.OnDBusPropertiesChanged("", properties, vector<string>());
+ VerifyDefaultProperties();
+
+ properties[MM_BEARER_PROPERTY_CONNECTED].writer().append_bool(true);
+ bearer_.OnDBusPropertiesChanged("", properties, vector<string>());
+ VerifyDefaultProperties();
+
+ // Update 'interface' property.
+ properties.clear();
+ properties[MM_BEARER_PROPERTY_INTERFACE].writer().append_string(
+ kDataInterface);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(kDataInterface, bearer_.data_interface());
+
+ // Update 'connected' property.
+ properties.clear();
+ properties[MM_BEARER_PROPERTY_CONNECTED].writer().append_bool(true);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_TRUE(bearer_.connected());
+ // 'interface' property remains unchanged.
+ EXPECT_EQ(kDataInterface, bearer_.data_interface());
+
+ DBus::MessageIter writer;
+
+ // Update 'ip4config' property.
+ properties.clear();
+ writer = properties[MM_BEARER_PROPERTY_IP4CONFIG].writer();
+ writer << ConstructIPv4ConfigProperties(MM_BEARER_IP_METHOD_UNKNOWN);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(IPConfig::kMethodUnknown, bearer_.ipv4_config_method());
+
+ properties.clear();
+ writer = properties[MM_BEARER_PROPERTY_IP4CONFIG].writer();
+ writer << ConstructIPv4ConfigProperties(MM_BEARER_IP_METHOD_PPP);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(IPConfig::kMethodPPP, bearer_.ipv4_config_method());
+
+ properties.clear();
+ writer = properties[MM_BEARER_PROPERTY_IP4CONFIG].writer();
+ writer << ConstructIPv4ConfigProperties(MM_BEARER_IP_METHOD_STATIC);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(IPConfig::kMethodStatic, bearer_.ipv4_config_method());
+ VerifyStaticIPv4ConfigMethodAndProperties();
+
+ properties.clear();
+ writer = properties[MM_BEARER_PROPERTY_IP4CONFIG].writer();
+ writer << ConstructIPv4ConfigProperties(MM_BEARER_IP_METHOD_DHCP);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(IPConfig::kMethodDHCP, bearer_.ipv4_config_method());
+
+ // Update 'ip6config' property.
+ properties.clear();
+ writer = properties[MM_BEARER_PROPERTY_IP6CONFIG].writer();
+ writer << ConstructIPv6ConfigProperties(MM_BEARER_IP_METHOD_UNKNOWN);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(IPConfig::kMethodUnknown, bearer_.ipv6_config_method());
+
+ properties.clear();
+ writer = properties[MM_BEARER_PROPERTY_IP6CONFIG].writer();
+ writer << ConstructIPv6ConfigProperties(MM_BEARER_IP_METHOD_PPP);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(IPConfig::kMethodPPP, bearer_.ipv6_config_method());
+
+ properties.clear();
+ writer = properties[MM_BEARER_PROPERTY_IP6CONFIG].writer();
+ writer << ConstructIPv6ConfigProperties(MM_BEARER_IP_METHOD_STATIC);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(IPConfig::kMethodStatic, bearer_.ipv6_config_method());
+ VerifyStaticIPv6ConfigMethodAndProperties();
+
+ properties.clear();
+ writer = properties[MM_BEARER_PROPERTY_IP6CONFIG].writer();
+ writer << ConstructIPv6ConfigProperties(MM_BEARER_IP_METHOD_DHCP);
+ bearer_.OnDBusPropertiesChanged(MM_DBUS_INTERFACE_BEARER, properties,
+ vector<string>());
+ EXPECT_EQ(IPConfig::kMethodDHCP, bearer_.ipv6_config_method());
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability.cc b/cellular/cellular_capability.cc
new file mode 100644
index 0000000..1d6df69
--- /dev/null
+++ b/cellular/cellular_capability.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 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/cellular_capability.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+#include "shill/property_accessor.h"
+
+using base::Closure;
+using std::string;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularCapability *c) {
+ return c->cellular()->GetRpcIdentifier();
+}
+}
+
+const char CellularCapability::kModemPropertyIMSI[] = "imsi";
+const char CellularCapability::kModemPropertyState[] = "State";
+// All timeout values are in milliseconds
+const int CellularCapability::kTimeoutActivate = 300000;
+const int CellularCapability::kTimeoutConnect = 45000;
+const int CellularCapability::kTimeoutDefault = 5000;
+const int CellularCapability::kTimeoutDisconnect = 45000;
+const int CellularCapability::kTimeoutEnable = 45000;
+const int CellularCapability::kTimeoutRegister = 90000;
+const int CellularCapability::kTimeoutReset = 90000;
+const int CellularCapability::kTimeoutScan = 120000;
+
+CellularCapability::CellularCapability(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info)
+ : cellular_(cellular),
+ proxy_factory_(proxy_factory),
+ modem_info_(modem_info) {}
+
+CellularCapability::~CellularCapability() {}
+
+void CellularCapability::OnUnsupportedOperation(const char *operation,
+ Error *error) {
+ string message("The ");
+ message.append(operation).append(" operation is not supported.");
+ Error::PopulateAndLog(error, Error::kNotSupported, message);
+}
+
+void CellularCapability::DisconnectCleanup() {}
+
+void CellularCapability::Activate(const string &carrier,
+ Error *error,
+ const ResultCallback &callback) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapability::CompleteActivation(Error *error) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+bool CellularCapability::IsServiceActivationRequired() const {
+ return false;
+}
+
+void CellularCapability::RegisterOnNetwork(
+ const string &/*network_id*/,
+ Error *error,
+ const ResultCallback &/*callback*/) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapability::RequirePIN(const std::string &/*pin*/,
+ bool /*require*/,
+ Error *error,
+ const ResultCallback &/*callback*/) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapability::EnterPIN(const string &/*pin*/,
+ Error *error,
+ const ResultCallback &/*callback*/) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapability::UnblockPIN(const string &/*unblock_code*/,
+ const string &/*pin*/,
+ Error *error,
+ const ResultCallback &/*callback*/) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapability::ChangePIN(const string &/*old_pin*/,
+ const string &/*new_pin*/,
+ Error *error,
+ const ResultCallback &/*callback*/) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapability::Scan(Error *error,
+ const ResultStringmapsCallback &callback) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapability::Reset(Error *error,
+ const ResultCallback &/*callback*/) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapability::SetCarrier(const std::string &/*carrier*/,
+ Error *error,
+ const ResultCallback &/*callback*/) {
+ OnUnsupportedOperation(__func__, error);
+}
+
+CellularBearer *CellularCapability::GetActiveBearer() const {
+ return nullptr;
+}
+
+bool CellularCapability::IsActivating() const {
+ return false;
+}
+
+bool CellularCapability::ShouldDetectOutOfCredit() const {
+ return false;
+}
+
+void CellularCapability::OnOperatorChanged() {
+ SLOG(this, 3) << __func__;
+ if (cellular()->service()) {
+ UpdateServiceOLP();
+ }
+}
+
+void CellularCapability::UpdateServiceOLP() {
+ SLOG(this, 3) << __func__;
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability.h b/cellular/cellular_capability.h
new file mode 100644
index 0000000..357aaa2
--- /dev/null
+++ b/cellular/cellular_capability.h
@@ -0,0 +1,301 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_CAPABILITY_H_
+#define SHILL_CELLULAR_CELLULAR_CAPABILITY_H_
+
+#include <string>
+#include <vector>
+
+#include <base/callback.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/callbacks.h"
+#include "shill/cellular/cellular.h"
+#include "shill/dbus_properties.h"
+#include "shill/metrics.h"
+
+namespace shill {
+
+class Cellular;
+class CellularBearer;
+class Error;
+class ModemInfo;
+class ProxyFactory;
+
+// Cellular devices instantiate subclasses of CellularCapability that
+// handle the specific modem technologies and capabilities.
+//
+// The CellularCapability is directly subclassed by:
+// * CelllularCapabilityUniversal which handles all modems managed by
+// a modem manager using the the org.chromium.ModemManager1 DBUS
+// interface.
+// * CellularCapabilityClassic which handles all modems managed by a
+// modem manager using the older org.chromium.ModemManager DBUS
+// interface. This class is further subclassed to represent CDMA
+// and GSM modems.
+//
+// Pictorially:
+//
+// CellularCapability
+// |
+// |-- CellularCapabilityUniversal
+// | |
+// | |-- CellularCapabilityUniversalCDMA
+// |
+// |-- CellularCapabilityClassic
+// |
+// |-- CellularCapabilityGSM
+// |
+// |-- CellularCapabilityCDMA
+//
+// TODO(armansito): Currently, 3GPP logic is handled by
+// CellularCapabilityUniversal. Eventually CellularCapabilityUniversal will
+// only serve as the abstract base class for ModemManager1 3GPP and CDMA
+// capabilities.
+class CellularCapability {
+ public:
+ static const int kTimeoutActivate;
+ static const int kTimeoutConnect;
+ static const int kTimeoutDefault;
+ static const int kTimeoutDisconnect;
+ static const int kTimeoutEnable;
+ static const int kTimeoutRegister;
+ static const int kTimeoutReset;
+ static const int kTimeoutScan;
+
+ static const char kModemPropertyIMSI[];
+ static const char kModemPropertyState[];
+
+ // |cellular| is the parent Cellular device.
+ CellularCapability(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info);
+ virtual ~CellularCapability();
+
+ virtual std::string GetTypeString() const = 0;
+
+ // Called when the modem manager has sent a property change notification
+ // signal over DBus.
+ virtual void OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties) = 0;
+
+ // -------------------------------------------------------------------------
+ // Modem management
+ // -------------------------------------------------------------------------
+
+ // StartModem attempts to put the modem in a state in which it is usable for
+ // creating services and establishing connections (if network conditions
+ // permit). It potentially consists of multiple non-blocking calls to the
+ // modem-manager server. After each call, control is passed back up to the
+ // main loop. Each time a reply to a non-blocking call is received, the
+ // operation advances to the next step, until either an error occurs in one of
+ // them, or all the steps have been completed, at which point StartModem() is
+ // finished.
+ virtual void StartModem(Error *error, const ResultCallback &callback) = 0;
+
+ // StopModem disconnects and disables a modem asynchronously. |callback| is
+ // invoked when this completes and the result is passed to the callback.
+ virtual void StopModem(Error *error, const ResultCallback &callback) = 0;
+
+ // Resets the modem.
+ //
+ // The default implementation fails by returning kNotSupported via |error|.
+ virtual void Reset(Error *error, const ResultCallback &callback);
+
+ // Checks to see if all proxies have been initialized.
+ virtual bool AreProxiesInitialized() const = 0;
+
+ // -------------------------------------------------------------------------
+ // Activation
+ // -------------------------------------------------------------------------
+
+ // Returns true if service activation is required.
+ //
+ // The default implementation returns false.
+ virtual bool IsServiceActivationRequired() const;
+
+ // Returns true if the modem is being activated.
+ //
+ // The default implementation returns false.
+ virtual bool IsActivating() const;
+
+ // Activates the modem.
+ //
+ // The default implementation fails by returning kNotSupported via |error|.
+ virtual void Activate(const std::string &carrier,
+ Error *error, const ResultCallback &callback);
+
+ // Initiates the necessary to steps to verify that the cellular service has
+ // been activated. Once these steps have been completed, the service should
+ // be marked as activated.
+ //
+ // The default implementation fails by returning kNotSupported via |error|.
+ virtual void CompleteActivation(Error *error);
+
+ // -------------------------------------------------------------------------
+ // Network service and registration
+ // -------------------------------------------------------------------------
+
+ // Configures the modem to support the |carrier|.
+ //
+ // The default implementation fails by returning kNotSupported via |error|.
+ virtual void SetCarrier(const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback);
+
+ // Asks the modem to scan for networks.
+ //
+ // The default implementation fails by returning kNotSupported via |error|.
+ //
+ // Subclasses should implement this by fetching scan results asynchronously.
+ // When the results are ready, update the kFoundNetworksProperty and send a
+ // property change notification. Finally, callback must be invoked to inform
+ // the caller that the scan has completed.
+ //
+ // Errors are not generally reported, but on error the kFoundNetworksProperty
+ // should be cleared and a property change notification sent out.
+ //
+ // TODO(jglasgow): Refactor to reuse code by putting notification logic into
+ // Cellular or CellularCapability.
+ //
+ // TODO(jglasgow): Implement real error handling.
+ virtual void Scan(Error *error, const ResultStringmapsCallback &callback);
+
+ // Registers on a network with |network_id|.
+ virtual void RegisterOnNetwork(const std::string &network_id,
+ Error *error,
+ const ResultCallback &callback);
+
+ // Returns true if the modem is registered on a network, which can be a home
+ // or roaming network. It is possible that we cannot determine whether it is
+ // a home or roaming network, but we still consider the modem is registered.
+ virtual bool IsRegistered() const = 0;
+
+ // If we are informed by means of something other than a signal indicating
+ // a registration state change that the modem has unregistered from the
+ // network, we need to update the network-type-specific capability object.
+ virtual void SetUnregistered(bool searching) = 0;
+
+ // Invoked by the parent Cellular device when a new service is created.
+ virtual void OnServiceCreated() = 0;
+
+ // Hook called by the Cellular device when either the Home Provider or the
+ // Serving Operator changes. Default implementation calls other hooks declared
+ // below. Overrides should chain up to this function.
+ // Note: This may be called before |CellularService| is created.
+ virtual void OnOperatorChanged();
+ virtual void UpdateServiceOLP();
+
+ // Returns an empty string if the network technology is unknown.
+ virtual std::string GetNetworkTechnologyString() const = 0;
+
+ virtual std::string GetRoamingStateString() const = 0;
+
+ // Should this device allow roaming?
+ // The decision to allow roaming or not is based on the home provider as well
+ // as on the user modifiable "allow_roaming" property.
+ virtual bool AllowRoaming() = 0;
+
+ // Returns true if the cellular device should initiate passive traffic
+ // monitoring to trigger active out-of-credit detection checks. The default
+ // implementation returns false by default.
+ virtual bool ShouldDetectOutOfCredit() const;
+
+ // TODO(armansito): Remove this method once cromo is deprecated.
+ virtual void GetSignalQuality() = 0;
+
+ // -------------------------------------------------------------------------
+ // Connection management
+ // -------------------------------------------------------------------------
+
+ // Fills |properties| with properties for establishing a connection, which
+ // will be passed to Connect().
+ virtual void SetupConnectProperties(DBusPropertiesMap *properties) = 0;
+
+ // Connects the modem to a network based on the connection properties
+ // specified by |properties|.
+ virtual void Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback) = 0;
+
+ // Disconnects the modem from a network.
+ virtual void Disconnect(Error *error, const ResultCallback &callback) = 0;
+
+ // Called when a disconnect operation completes, successful or not.
+ //
+ // The default implementation does nothing.
+ virtual void DisconnectCleanup();
+
+ // Returns a pointer to the current active bearer object or nullptr if no
+ // active bearer exists. The returned bearer object is managed by this
+ // capability object. This implementation returns nullptr by default.
+ virtual CellularBearer *GetActiveBearer() const;
+
+ // -------------------------------------------------------------------------
+ // SIM PIN management
+ // -------------------------------------------------------------------------
+
+ // The default implementation fails by returning kNotSupported via |error|.
+ virtual void RequirePIN(const std::string &pin,
+ bool require,
+ Error *error,
+ const ResultCallback &callback);
+
+ virtual void EnterPIN(const std::string &pin,
+ Error *error,
+ const ResultCallback &callback);
+
+ virtual void UnblockPIN(const std::string &unblock_code,
+ const std::string &pin,
+ Error *error,
+ const ResultCallback &callback);
+
+ virtual void ChangePIN(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error,
+ const ResultCallback &callback);
+
+ // -------------------------------------------------------------------------
+
+ Cellular *cellular() const { return cellular_; }
+ ProxyFactory *proxy_factory() const { return proxy_factory_; }
+ ModemInfo *modem_info() const { return modem_info_; }
+
+ protected:
+ // Releases all proxies held by the object. This is most useful during unit
+ // tests.
+ virtual void ReleaseProxies() = 0;
+
+ static void OnUnsupportedOperation(const char *operation, Error *error);
+
+ // Accessor for subclasses to read the 'allow roaming' property.
+ bool allow_roaming_property() const {
+ return cellular_->allow_roaming_property();
+ }
+
+ private:
+ friend class CellularCapabilityGSMTest;
+ friend class CellularCapabilityTest;
+ friend class CellularCapabilityUniversalTest;
+ friend class CellularCapabilityUniversalCDMATest;
+ friend class CellularTest;
+ FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateActiveBearer);
+ FRIEND_TEST(CellularTest, Connect);
+ FRIEND_TEST(CellularTest, TearDown);
+
+ Cellular *cellular_;
+ ProxyFactory *proxy_factory_;
+ ModemInfo *modem_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(CellularCapability);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_CAPABILITY_H_
diff --git a/cellular/cellular_capability_cdma.cc b/cellular/cellular_capability_cdma.cc
new file mode 100644
index 0000000..255d933
--- /dev/null
+++ b/cellular/cellular_capability_cdma.cc
@@ -0,0 +1,404 @@
+// Copyright (c) 2012 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/cellular_capability_cdma.h"
+
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <mm/mm-modem.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/logging.h"
+#include "shill/proxy_factory.h"
+
+using base::Bind;
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularCapabilityCDMA *c) {
+ return c->cellular()->GetRpcIdentifier();
+}
+}
+
+// static
+const char CellularCapabilityCDMA::kPhoneNumber[] = "#777";
+
+CellularCapabilityCDMA::CellularCapabilityCDMA(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info)
+ : CellularCapabilityClassic(cellular, proxy_factory, modem_info),
+ weak_ptr_factory_(this),
+ activation_starting_(false),
+ activation_state_(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED),
+ registration_state_evdo_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
+ registration_state_1x_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) {
+ SLOG(this, 2) << "Cellular capability constructed: CDMA";
+}
+
+CellularCapabilityCDMA::~CellularCapabilityCDMA() {}
+
+void CellularCapabilityCDMA::InitProxies() {
+ CellularCapabilityClassic::InitProxies();
+ proxy_.reset(proxy_factory()->CreateModemCDMAProxy(
+ cellular()->dbus_path(), cellular()->dbus_owner()));
+ proxy_->set_signal_quality_callback(
+ Bind(&CellularCapabilityCDMA::OnSignalQualitySignal,
+ weak_ptr_factory_.GetWeakPtr()));
+ proxy_->set_activation_state_callback(
+ Bind(&CellularCapabilityCDMA::OnActivationStateChangedSignal,
+ weak_ptr_factory_.GetWeakPtr()));
+ proxy_->set_registration_state_callback(
+ Bind(&CellularCapabilityCDMA::OnRegistrationStateChangedSignal,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+string CellularCapabilityCDMA::GetTypeString() const {
+ return kTechnologyFamilyCdma;
+}
+
+void CellularCapabilityCDMA::StartModem(Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ InitProxies();
+
+ CellularTaskList *tasks = new CellularTaskList();
+ ResultCallback cb =
+ Bind(&CellularCapabilityCDMA::StepCompletedCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
+ if (!cellular()->IsUnderlyingDeviceEnabled())
+ tasks->push_back(Bind(&CellularCapabilityCDMA::EnableModem,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ tasks->push_back(Bind(&CellularCapabilityCDMA::GetModemStatus,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ tasks->push_back(Bind(&CellularCapabilityCDMA::GetMEID,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ tasks->push_back(Bind(&CellularCapabilityCDMA::GetModemInfo,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ tasks->push_back(Bind(&CellularCapabilityCDMA::FinishEnable,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+
+ RunNextStep(tasks);
+}
+
+void CellularCapabilityCDMA::ReleaseProxies() {
+ CellularCapabilityClassic::ReleaseProxies();
+ proxy_.reset();
+}
+
+bool CellularCapabilityCDMA::AreProxiesInitialized() const {
+ return (CellularCapabilityClassic::AreProxiesInitialized() && proxy_.get());
+}
+
+bool CellularCapabilityCDMA::AllowRoaming() {
+ return allow_roaming_property();
+}
+
+
+void CellularCapabilityCDMA::OnServiceCreated() {
+ SLOG(this, 2) << __func__;
+ cellular()->service()->SetUsageURL(usage_url_);
+ cellular()->service()->SetActivationType(
+ CellularService::kActivationTypeOTASP);
+ HandleNewActivationState(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR);
+}
+
+void CellularCapabilityCDMA::UpdateStatus(const DBusPropertiesMap &properties) {
+ string carrier;
+ DBusProperties::GetUint32(
+ properties, "activation_state", &activation_state_);
+ // TODO(petkov): For now, get the payment and usage URLs from ModemManager to
+ // match flimflam. In the future, get these from an alternative source (e.g.,
+ // database, carrier-specific properties, etc.).
+ UpdateOnlinePortal(properties);
+ uint16_t prl_version;
+ if (DBusProperties::GetUint16(properties, "prl_version", &prl_version))
+ cellular()->set_prl_version(prl_version);
+}
+
+void CellularCapabilityCDMA::UpdateServiceOLP() {
+ SLOG(this, 3) << __func__;
+ // All OLP changes are routed up to the Home Provider.
+ if (!cellular()->home_provider_info()->IsMobileNetworkOperatorKnown()) {
+ return;
+ }
+
+ const vector<MobileOperatorInfo::OnlinePortal> &olp_list =
+ cellular()->home_provider_info()->olp_list();
+ if (olp_list.empty()) {
+ return;
+ }
+
+ if (olp_list.size() > 1) {
+ SLOG(this, 1) << "Found multiple online portals. Choosing the first.";
+ }
+ cellular()->service()->SetOLP(olp_list[0].url,
+ olp_list[0].method,
+ olp_list[0].post_data);
+}
+
+void CellularCapabilityCDMA::SetupConnectProperties(
+ DBusPropertiesMap *properties) {
+ (*properties)[kConnectPropertyPhoneNumber].writer().append_string(
+ kPhoneNumber);
+}
+
+void CellularCapabilityCDMA::Activate(const string &carrier,
+ Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 2) << __func__ << "(" << carrier << ")";
+ // We're going to trigger something which leads to an activation.
+ activation_starting_ = true;
+ if (cellular()->state() == Cellular::kStateEnabled ||
+ cellular()->state() == Cellular::kStateRegistered) {
+ ActivationResultCallback activation_callback =
+ Bind(&CellularCapabilityCDMA::OnActivateReply,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ proxy_->Activate(carrier, error, activation_callback, kTimeoutActivate);
+ } else if (cellular()->state() == Cellular::kStateConnected ||
+ cellular()->state() == Cellular::kStateLinked) {
+ pending_activation_callback_ = callback;
+ pending_activation_carrier_ = carrier;
+ cellular()->Disconnect(error, __func__);
+ } else {
+ Error::PopulateAndLog(error, Error::kInvalidArguments,
+ "Unable to activate in " +
+ Cellular::GetStateString(cellular()->state()));
+ activation_starting_ = false;
+ }
+}
+
+void CellularCapabilityCDMA::HandleNewActivationState(uint32_t error) {
+ SLOG(this, 2) << __func__ << "(" << error << ")";
+ if (!cellular()->service().get()) {
+ LOG(ERROR) << "In " << __func__ << "(): service is null.";
+ return;
+ }
+ cellular()->service()->SetActivationState(
+ GetActivationStateString(activation_state_));
+ cellular()->service()->set_error(GetActivationErrorString(error));
+}
+
+void CellularCapabilityCDMA::DisconnectCleanup() {
+ CellularCapabilityClassic::DisconnectCleanup();
+ if (pending_activation_callback_.is_null()) {
+ return;
+ }
+ if (cellular()->state() == Cellular::kStateEnabled ||
+ cellular()->state() == Cellular::kStateRegistered) {
+ Error ignored_error;
+ Activate(pending_activation_carrier_,
+ &ignored_error,
+ pending_activation_callback_);
+ } else {
+ Error error;
+ Error::PopulateAndLog(
+ &error,
+ Error::kOperationFailed,
+ "Tried to disconnect before activating cellular service and failed");
+ HandleNewActivationState(MM_MODEM_CDMA_ACTIVATION_ERROR_UNKNOWN);
+ activation_starting_ = false;
+ pending_activation_callback_.Run(error);
+ }
+ pending_activation_callback_.Reset();
+ pending_activation_carrier_.clear();
+}
+
+// static
+string CellularCapabilityCDMA::GetActivationStateString(uint32_t state) {
+ switch (state) {
+ case MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED:
+ return kActivationStateActivated;
+ case MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING:
+ return kActivationStateActivating;
+ case MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED:
+ return kActivationStateNotActivated;
+ case MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED:
+ return kActivationStatePartiallyActivated;
+ default:
+ return kActivationStateUnknown;
+ }
+}
+
+// static
+string CellularCapabilityCDMA::GetActivationErrorString(uint32_t error) {
+ switch (error) {
+ case MM_MODEM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE:
+ return kErrorNeedEvdo;
+ case MM_MODEM_CDMA_ACTIVATION_ERROR_ROAMING:
+ return kErrorNeedHomeNetwork;
+ case MM_MODEM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT:
+ case MM_MODEM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED:
+ case MM_MODEM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED:
+ return kErrorOtaspFailed;
+ case MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR:
+ return "";
+ case MM_MODEM_CDMA_ACTIVATION_ERROR_NO_SIGNAL:
+ default:
+ return kErrorActivationFailed;
+ }
+}
+
+void CellularCapabilityCDMA::GetMEID(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ if (cellular()->meid().empty()) {
+ // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
+ cellular()->set_meid(proxy_->MEID());
+ SLOG(this, 2) << "MEID: " << cellular()->meid();
+ }
+ callback.Run(Error());
+}
+
+void CellularCapabilityCDMA::GetProperties(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ // No properties.
+ callback.Run(Error());
+}
+
+bool CellularCapabilityCDMA::IsActivating() const {
+ return activation_starting_ ||
+ activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+}
+
+bool CellularCapabilityCDMA::IsRegistered() const {
+ return registration_state_evdo_ != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN ||
+ registration_state_1x_ != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+}
+
+void CellularCapabilityCDMA::SetUnregistered(bool searching) {
+ registration_state_evdo_ = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ registration_state_1x_ = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+}
+
+string CellularCapabilityCDMA::GetNetworkTechnologyString() const {
+ if (registration_state_evdo_ != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) {
+ return kNetworkTechnologyEvdo;
+ }
+ if (registration_state_1x_ != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) {
+ return kNetworkTechnology1Xrtt;
+ }
+ return "";
+}
+
+string CellularCapabilityCDMA::GetRoamingStateString() const {
+ uint32_t state = registration_state_evdo_;
+ if (state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) {
+ state = registration_state_1x_;
+ }
+ switch (state) {
+ case MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN:
+ case MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED:
+ break;
+ case MM_MODEM_CDMA_REGISTRATION_STATE_HOME:
+ return kRoamingStateHome;
+ case MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING:
+ return kRoamingStateRoaming;
+ default:
+ NOTREACHED();
+ }
+ return kRoamingStateUnknown;
+}
+
+void CellularCapabilityCDMA::GetSignalQuality() {
+ SLOG(this, 2) << __func__;
+ SignalQualityCallback callback =
+ Bind(&CellularCapabilityCDMA::OnGetSignalQualityReply,
+ weak_ptr_factory_.GetWeakPtr());
+ proxy_->GetSignalQuality(nullptr, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityCDMA::GetRegistrationState() {
+ SLOG(this, 2) << __func__;
+ RegistrationStateCallback callback =
+ Bind(&CellularCapabilityCDMA::OnGetRegistrationStateReply,
+ weak_ptr_factory_.GetWeakPtr());
+ proxy_->GetRegistrationState(nullptr, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityCDMA::OnActivateReply(
+ const ResultCallback &callback, uint32_t status, const Error &error) {
+ activation_starting_ = false;
+ if (error.IsSuccess()) {
+ if (status == MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR) {
+ activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+ } else {
+ LOG(WARNING) << "Modem activation failed with status: "
+ << GetActivationErrorString(status) << " (" << status << ")";
+ }
+ HandleNewActivationState(status);
+ } else {
+ LOG(ERROR) << "Activate() failed with error: " << error;
+ }
+ callback.Run(error);
+}
+
+void CellularCapabilityCDMA::OnGetRegistrationStateReply(
+ uint32_t state_1x, uint32_t state_evdo, const Error &error) {
+ SLOG(this, 2) << __func__;
+ if (error.IsSuccess())
+ OnRegistrationStateChangedSignal(state_1x, state_evdo);
+}
+
+void CellularCapabilityCDMA::OnGetSignalQualityReply(uint32_t quality,
+ const Error &error) {
+ if (error.IsSuccess())
+ OnSignalQualitySignal(quality);
+}
+
+void CellularCapabilityCDMA::OnActivationStateChangedSignal(
+ uint32_t activation_state,
+ uint32_t activation_error,
+ const DBusPropertiesMap &status_changes) {
+ SLOG(this, 2) << __func__;
+ string prop_value;
+
+ if (DBusProperties::GetString(status_changes, "mdn", &prop_value))
+ cellular()->set_mdn(prop_value);
+ if (DBusProperties::GetString(status_changes, "min", &prop_value))
+ cellular()->set_min(prop_value);
+
+ UpdateOnlinePortal(status_changes);
+ activation_state_ = activation_state;
+ HandleNewActivationState(activation_error);
+}
+
+void CellularCapabilityCDMA::OnRegistrationStateChangedSignal(
+ uint32_t state_1x, uint32_t state_evdo) {
+ SLOG(this, 2) << __func__;
+ registration_state_1x_ = state_1x;
+ registration_state_evdo_ = state_evdo;
+ cellular()->HandleNewRegistrationState();
+}
+
+void CellularCapabilityCDMA::OnSignalQualitySignal(uint32_t strength) {
+ cellular()->HandleNewSignalQuality(strength);
+}
+
+void CellularCapabilityCDMA::UpdateOnlinePortal(
+ const DBusPropertiesMap &properties) {
+ // Treat the three updates atomically: Only update the serving operator when
+ // all three are known:
+ string olp_url, olp_method, olp_post_data;
+ if (DBusProperties::GetString(properties, "payment_url", &olp_url) &&
+ DBusProperties::GetString(properties,
+ "payment_url_method", &olp_method) &&
+ DBusProperties::GetString(properties,
+ "payment_url_postdata",
+ &olp_post_data)) {
+ cellular()->home_provider_info()->UpdateOnlinePortal(olp_url,
+ olp_method,
+ olp_post_data);
+ }
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_cdma.h b/cellular/cellular_capability_cdma.h
new file mode 100644
index 0000000..eb5436f
--- /dev/null
+++ b/cellular/cellular_capability_cdma.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_CAPABILITY_CDMA_H_
+#define SHILL_CELLULAR_CELLULAR_CAPABILITY_CDMA_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/memory/weak_ptr.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/cellular/cellular_capability.h"
+#include "shill/cellular/cellular_capability_classic.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/modem_cdma_proxy_interface.h"
+
+namespace shill {
+
+class ModemInfo;
+
+class CellularCapabilityCDMA : public CellularCapabilityClassic {
+ public:
+ CellularCapabilityCDMA(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info);
+ ~CellularCapabilityCDMA() override;
+
+ // Inherited from CellularCapability.
+ std::string GetTypeString() const override;
+ void StartModem(Error *error,
+ const ResultCallback &callback) override;
+ bool AreProxiesInitialized() const override;
+ void Activate(const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback) override;
+ bool IsActivating() const override;
+ bool IsRegistered() const override;
+ void SetUnregistered(bool searching) override;
+ void OnServiceCreated() override;
+ std::string GetNetworkTechnologyString() const override;
+ std::string GetRoamingStateString() const override;
+ bool AllowRoaming() override;
+ void GetSignalQuality() override;
+ void SetupConnectProperties(DBusPropertiesMap *properties) override;
+ void DisconnectCleanup() override;
+
+ // Inherited from CellularCapabilityClassic.
+ void GetRegistrationState() override;
+ void GetProperties(const ResultCallback &callback) override;
+
+ virtual void GetMEID(const ResultCallback &callback);
+
+ uint32_t activation_state() const { return activation_state_; }
+ uint32_t registration_state_evdo() const { return registration_state_evdo_; }
+ uint32_t registration_state_1x() const { return registration_state_1x_; }
+
+ protected:
+ // Inherited from CellularCapabilityClassic.
+ void InitProxies() override;
+ void ReleaseProxies() override;
+ void UpdateStatus(const DBusPropertiesMap &properties) override;
+
+ private:
+ friend class CellularCapabilityCDMATest;
+ friend class CellularTest;
+ FRIEND_TEST(CellularCapabilityCDMATest, Activate);
+ FRIEND_TEST(CellularCapabilityCDMATest, ActivateError);
+ FRIEND_TEST(CellularCapabilityCDMATest, GetActivationStateString);
+ FRIEND_TEST(CellularCapabilityCDMATest, GetActivationErrorString);
+ FRIEND_TEST(CellularServiceTest, IsAutoConnectable);
+ FRIEND_TEST(CellularTest, CreateService);
+
+ static const char kPhoneNumber[];
+
+ void HandleNewActivationState(uint32_t error);
+
+ static std::string GetActivationStateString(uint32_t state);
+ static std::string GetActivationErrorString(uint32_t error);
+
+ // Signal callbacks from the Modem.CDMA interface
+ void OnActivationStateChangedSignal(
+ uint32_t activation_state,
+ uint32_t activation_error,
+ const DBusPropertiesMap &status_changes);
+ void OnRegistrationStateChangedSignal(
+ uint32_t state_1x, uint32_t state_evdo);
+ void OnSignalQualitySignal(uint32_t strength);
+
+ // Method reply callbacks from the Modem.CDMA interface
+ void OnActivateReply(const ResultCallback &callback,
+ uint32_t status, const Error &error);
+
+ void OnGetRegistrationStateReply(uint32_t state_1x, uint32_t state_evdo,
+ const Error &error);
+ void OnGetSignalQualityReply(uint32_t strength, const Error &error);
+
+ std::unique_ptr<ModemCDMAProxyInterface> proxy_;
+ base::WeakPtrFactory<CellularCapabilityCDMA> weak_ptr_factory_;
+
+ // Helper method to extract the online portal information from properties.
+ void UpdateOnlinePortal(const DBusPropertiesMap &properties);
+ void UpdateServiceOLP() override;
+
+ bool activation_starting_;
+ ResultCallback pending_activation_callback_;
+ std::string pending_activation_carrier_;
+ uint32_t activation_state_;
+ uint32_t registration_state_evdo_;
+ uint32_t registration_state_1x_;
+ std::string usage_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(CellularCapabilityCDMA);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_CAPABILITY_CDMA_H_
diff --git a/cellular/cellular_capability_cdma_unittest.cc b/cellular/cellular_capability_cdma_unittest.cc
new file mode 100644
index 0000000..3f8a8be
--- /dev/null
+++ b/cellular/cellular_capability_cdma_unittest.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2012 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/cellular_capability_cdma.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+#include <mm/mm-modem.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/mock_cellular.h"
+#include "shill/cellular/mock_modem_cdma_proxy.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/cellular/mock_modem_proxy.h"
+#include "shill/error.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_adaptors.h"
+#include "shill/proxy_factory.h"
+
+using base::Bind;
+using base::Unretained;
+using std::string;
+using testing::_;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::StrEq;
+
+namespace shill {
+
+class CellularCapabilityCDMATest : public testing::Test {
+ public:
+ CellularCapabilityCDMATest()
+ : modem_info_(nullptr, &dispatcher_, nullptr, nullptr, nullptr),
+ cellular_(new MockCellular(&modem_info_,
+ "",
+ "",
+ 0,
+ Cellular::kTypeCDMA,
+ "",
+ "",
+ "",
+ ProxyFactory::GetInstance())),
+ classic_proxy_(new MockModemProxy()),
+ proxy_(new MockModemCDMAProxy()),
+ capability_(nullptr) {
+ modem_info_.metrics()->RegisterDevice(cellular_->interface_index(),
+ Technology::kCellular);
+ }
+
+ virtual ~CellularCapabilityCDMATest() {
+ cellular_->service_ = nullptr;
+ capability_ = nullptr;
+ }
+
+ virtual void SetUp() {
+ capability_ =
+ dynamic_cast<CellularCapabilityCDMA *>(cellular_->capability_.get());
+ }
+
+ void InvokeActivate(const string &carrier, Error *error,
+ const ActivationResultCallback &callback,
+ int timeout) {
+ callback.Run(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR, Error());
+ }
+ void InvokeActivateError(const string &carrier, Error *error,
+ const ActivationResultCallback &callback,
+ int timeout) {
+ callback.Run(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_SIGNAL, Error());
+ }
+ void InvokeDisconnect(Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeDisconnectError(Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ Error err(Error::kOperationFailed);
+ callback.Run(err);
+ }
+ void InvokeGetSignalQuality(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout) {
+ callback.Run(kStrength, Error());
+ }
+ void InvokeGetRegistrationState(Error *error,
+ const RegistrationStateCallback &callback,
+ int timeout) {
+ callback.Run(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+ Error());
+ }
+
+ MOCK_METHOD1(TestCallback, void(const Error &error));
+
+ protected:
+ static const char kMEID[];
+ static const char kTestCarrier[];
+ static const unsigned int kStrength;
+
+ bool IsActivationStarting() const {
+ return capability_->activation_starting_;
+ }
+
+ void SetRegistrationStateEVDO(uint32_t state) {
+ capability_->registration_state_evdo_ = state;
+ }
+
+ void SetRegistrationState1x(uint32_t state) {
+ capability_->registration_state_1x_ = state;
+ }
+
+ void SetProxy() {
+ capability_->proxy_.reset(proxy_.release());
+ capability_->CellularCapabilityClassic::proxy_.reset(
+ classic_proxy_.release());
+ }
+
+ void SetService() {
+ cellular_->service_ = new CellularService(&modem_info_, cellular_);
+ }
+
+ void SetDeviceState(Cellular::State state) {
+ cellular_->state_ = state;
+ }
+
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ scoped_refptr<MockCellular> cellular_;
+ std::unique_ptr<MockModemProxy> classic_proxy_;
+ std::unique_ptr<MockModemCDMAProxy> proxy_;
+ CellularCapabilityCDMA *capability_; // Owned by |cellular_|.
+};
+
+const char CellularCapabilityCDMATest::kMEID[] = "D1234567EF8901";
+const char CellularCapabilityCDMATest::kTestCarrier[] = "The Cellular Carrier";
+const unsigned int CellularCapabilityCDMATest::kStrength = 90;
+
+TEST_F(CellularCapabilityCDMATest, PropertyStore) {
+ EXPECT_TRUE(cellular_->store().Contains(kPRLVersionProperty));
+}
+
+TEST_F(CellularCapabilityCDMATest, Activate) {
+ SetDeviceState(Cellular::kStateEnabled);
+ EXPECT_CALL(*proxy_, Activate(kTestCarrier, _, _,
+ CellularCapability::kTimeoutActivate))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityCDMATest::InvokeActivate));
+ EXPECT_CALL(*this, TestCallback(_));
+ SetProxy();
+ SetService();
+ capability_->Activate(kTestCarrier, nullptr,
+ Bind(&CellularCapabilityCDMATest::TestCallback, Unretained(this)));
+ EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING,
+ capability_->activation_state());
+ EXPECT_EQ(kActivationStateActivating,
+ cellular_->service()->activation_state());
+ EXPECT_EQ("", cellular_->service()->error());
+}
+
+TEST_F(CellularCapabilityCDMATest, ActivateWhileConnected) {
+ SetDeviceState(Cellular::kStateConnected);
+ {
+ InSequence dummy;
+
+ EXPECT_CALL(*cellular_, Disconnect(_, StrEq("Activate")));
+ EXPECT_CALL(*proxy_, Activate(kTestCarrier, _, _,
+ CellularCapability::kTimeoutActivate))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityCDMATest::InvokeActivate));
+ EXPECT_CALL(*this, TestCallback(_));
+ }
+ SetProxy();
+ SetService();
+ Error error;
+ capability_->Activate(kTestCarrier, &error,
+ Bind(&CellularCapabilityCDMATest::TestCallback, Unretained(this)));
+ // So now we should be "activating" while we wait for a disconnect.
+ EXPECT_TRUE(IsActivationStarting());
+ EXPECT_TRUE(capability_->IsActivating());
+ EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED,
+ capability_->activation_state());
+ // Simulate a disconnect.
+ SetDeviceState(Cellular::kStateRegistered);
+ capability_->DisconnectCleanup();
+ // Now the modem is actually activating.
+ EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING,
+ capability_->activation_state());
+ EXPECT_EQ(kActivationStateActivating,
+ cellular_->service()->activation_state());
+ EXPECT_EQ("", cellular_->service()->error());
+ EXPECT_FALSE(IsActivationStarting());
+ EXPECT_TRUE(capability_->IsActivating());
+}
+
+TEST_F(CellularCapabilityCDMATest, ActivateWhileConnectedButFail) {
+ SetDeviceState(Cellular::kStateConnected);
+ {
+ InSequence dummy;
+
+ EXPECT_CALL(*cellular_, Disconnect(_, StrEq("Activate")));
+ EXPECT_CALL(*proxy_, Activate(kTestCarrier, _, _,
+ CellularCapability::kTimeoutActivate))
+ .Times(0);
+ }
+ SetProxy();
+ SetService();
+ Error error;
+ capability_->Activate(kTestCarrier, &error,
+ Bind(&CellularCapabilityCDMATest::TestCallback, Unretained(this)));
+ // So now we should be "activating" while we wait for a disconnect.
+ EXPECT_TRUE(IsActivationStarting());
+ EXPECT_TRUE(capability_->IsActivating());
+ EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED,
+ capability_->activation_state());
+ // Similulate a failed disconnect (the modem is still connected!).
+ capability_->DisconnectCleanup();
+ EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED,
+ capability_->activation_state());
+ EXPECT_EQ(kActivationStateNotActivated,
+ cellular_->service()->activation_state());
+ EXPECT_EQ(kErrorActivationFailed, cellular_->service()->error());
+ EXPECT_FALSE(IsActivationStarting());
+ EXPECT_FALSE(capability_->IsActivating());
+}
+
+TEST_F(CellularCapabilityCDMATest, ActivateError) {
+ SetDeviceState(Cellular::kStateEnabled);
+ EXPECT_CALL(*proxy_, Activate(kTestCarrier, _, _,
+ CellularCapability::kTimeoutActivate))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityCDMATest::InvokeActivateError));
+ EXPECT_CALL(*this, TestCallback(_));
+ SetProxy();
+ SetService();
+ capability_->Activate(kTestCarrier, nullptr,
+ Bind(&CellularCapabilityCDMATest::TestCallback, Unretained(this)));
+ EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED,
+ capability_->activation_state());
+ EXPECT_EQ(kActivationStateNotActivated,
+ cellular_->service()->activation_state());
+ EXPECT_EQ(kErrorActivationFailed,
+ cellular_->service()->error());
+}
+
+TEST_F(CellularCapabilityCDMATest, GetActivationStateString) {
+ EXPECT_EQ(kActivationStateActivated,
+ CellularCapabilityCDMA::GetActivationStateString(
+ MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED));
+ EXPECT_EQ(kActivationStateActivating,
+ CellularCapabilityCDMA::GetActivationStateString(
+ MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING));
+ EXPECT_EQ(kActivationStateNotActivated,
+ CellularCapabilityCDMA::GetActivationStateString(
+ MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED));
+ EXPECT_EQ(kActivationStatePartiallyActivated,
+ CellularCapabilityCDMA::GetActivationStateString(
+ MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED));
+ EXPECT_EQ(kActivationStateUnknown,
+ CellularCapabilityCDMA::GetActivationStateString(123));
+}
+
+TEST_F(CellularCapabilityCDMATest, GetActivationErrorString) {
+ EXPECT_EQ(kErrorNeedEvdo,
+ CellularCapabilityCDMA::GetActivationErrorString(
+ MM_MODEM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE));
+ EXPECT_EQ(kErrorNeedHomeNetwork,
+ CellularCapabilityCDMA::GetActivationErrorString(
+ MM_MODEM_CDMA_ACTIVATION_ERROR_ROAMING));
+ EXPECT_EQ(kErrorOtaspFailed,
+ CellularCapabilityCDMA::GetActivationErrorString(
+ MM_MODEM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT));
+ EXPECT_EQ(kErrorOtaspFailed,
+ CellularCapabilityCDMA::GetActivationErrorString(
+ MM_MODEM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED));
+ EXPECT_EQ(kErrorOtaspFailed,
+ CellularCapabilityCDMA::GetActivationErrorString(
+ MM_MODEM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED));
+ EXPECT_EQ("",
+ CellularCapabilityCDMA::GetActivationErrorString(
+ MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR));
+ EXPECT_EQ(kErrorActivationFailed,
+ CellularCapabilityCDMA::GetActivationErrorString(
+ MM_MODEM_CDMA_ACTIVATION_ERROR_NO_SIGNAL));
+ EXPECT_EQ(kErrorActivationFailed,
+ CellularCapabilityCDMA::GetActivationErrorString(1234));
+}
+
+TEST_F(CellularCapabilityCDMATest, IsRegisteredEVDO) {
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
+ EXPECT_TRUE(capability_->IsRegistered());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
+ EXPECT_TRUE(capability_->IsRegistered());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+ EXPECT_TRUE(capability_->IsRegistered());
+}
+
+TEST_F(CellularCapabilityCDMATest, IsRegistered1x) {
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationState1x(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationState1x(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
+ EXPECT_TRUE(capability_->IsRegistered());
+ SetRegistrationState1x(MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
+ EXPECT_TRUE(capability_->IsRegistered());
+ SetRegistrationState1x(MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+ EXPECT_TRUE(capability_->IsRegistered());
+}
+
+TEST_F(CellularCapabilityCDMATest, GetNetworkTechnologyString) {
+ EXPECT_EQ("", capability_->GetNetworkTechnologyString());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
+ EXPECT_EQ(kNetworkTechnologyEvdo,
+ capability_->GetNetworkTechnologyString());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ SetRegistrationState1x(MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
+ EXPECT_EQ(kNetworkTechnology1Xrtt,
+ capability_->GetNetworkTechnologyString());
+}
+
+TEST_F(CellularCapabilityCDMATest, GetRoamingStateString) {
+ EXPECT_EQ(kRoamingStateUnknown,
+ capability_->GetRoamingStateString());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
+ EXPECT_EQ(kRoamingStateUnknown,
+ capability_->GetRoamingStateString());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
+ EXPECT_EQ(kRoamingStateHome, capability_->GetRoamingStateString());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+ EXPECT_EQ(kRoamingStateRoaming,
+ capability_->GetRoamingStateString());
+ SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ SetRegistrationState1x(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
+ EXPECT_EQ(kRoamingStateUnknown,
+ capability_->GetRoamingStateString());
+ SetRegistrationState1x(MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
+ EXPECT_EQ(kRoamingStateHome, capability_->GetRoamingStateString());
+ SetRegistrationState1x(MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+ EXPECT_EQ(kRoamingStateRoaming,
+ capability_->GetRoamingStateString());
+}
+
+TEST_F(CellularCapabilityCDMATest, GetSignalQuality) {
+ EXPECT_CALL(*proxy_,
+ GetSignalQuality(nullptr, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityCDMATest::InvokeGetSignalQuality));
+ SetProxy();
+ SetService();
+ EXPECT_EQ(0, cellular_->service()->strength());
+ capability_->GetSignalQuality();
+ EXPECT_EQ(kStrength, cellular_->service()->strength());
+}
+
+TEST_F(CellularCapabilityCDMATest, GetRegistrationState) {
+ EXPECT_FALSE(cellular_->service().get());
+ EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ capability_->registration_state_1x());
+ EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ capability_->registration_state_evdo());
+ EXPECT_CALL(*proxy_,
+ GetRegistrationState(nullptr, _,
+ CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(
+ this,
+ &CellularCapabilityCDMATest::InvokeGetRegistrationState));
+ SetProxy();
+ cellular_->state_ = Cellular::kStateEnabled;
+ EXPECT_CALL(*modem_info_.mock_manager(), RegisterService(_));
+ capability_->GetRegistrationState();
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
+ capability_->registration_state_1x());
+ EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+ capability_->registration_state_evdo());
+ EXPECT_TRUE(cellular_->service().get());
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_classic.cc b/cellular/cellular_capability_classic.cc
new file mode 100644
index 0000000..dc4250d
--- /dev/null
+++ b/cellular/cellular_capability_classic.cc
@@ -0,0 +1,354 @@
+// Copyright (c) 2012 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/cellular_capability_classic.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/modem_gobi_proxy_interface.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+#include "shill/property_accessor.h"
+#include "shill/proxy_factory.h"
+
+using base::Bind;
+using base::Callback;
+using base::Closure;
+using std::string;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularCapabilityClassic *c) {
+ return c->cellular()->GetRpcIdentifier();
+}
+}
+
+const char CellularCapabilityClassic::kConnectPropertyApn[] = "apn";
+const char CellularCapabilityClassic::kConnectPropertyApnUsername[] =
+ "username";
+const char CellularCapabilityClassic::kConnectPropertyApnPassword[] =
+ "password";
+const char CellularCapabilityClassic::kConnectPropertyHomeOnly[] = "home_only";
+const char CellularCapabilityClassic::kConnectPropertyPhoneNumber[] = "number";
+const char CellularCapabilityClassic::kModemPropertyEnabled[] = "Enabled";
+const int CellularCapabilityClassic::kTimeoutSetCarrierMilliseconds = 120000;
+
+static Cellular::ModemState ConvertClassicToModemState(uint32_t classic_state) {
+ ModemClassicState cstate = static_cast<ModemClassicState>(classic_state);
+ switch (cstate) {
+ case kModemClassicStateUnknown:
+ return Cellular::kModemStateUnknown;
+ case kModemClassicStateDisabled:
+ return Cellular::kModemStateDisabled;
+ case kModemClassicStateDisabling:
+ return Cellular::kModemStateDisabling;
+ case kModemClassicStateEnabling:
+ return Cellular::kModemStateEnabling;
+ case kModemClassicStateEnabled:
+ return Cellular::kModemStateEnabled;
+ case kModemClassicStateSearching:
+ return Cellular::kModemStateSearching;
+ case kModemClassicStateRegistered:
+ return Cellular::kModemStateRegistered;
+ case kModemClassicStateDisconnecting:
+ return Cellular::kModemStateDisconnecting;
+ case kModemClassicStateConnecting:
+ return Cellular::kModemStateConnecting;
+ case kModemClassicStateConnected:
+ return Cellular::kModemStateConnected;
+ default:
+ return Cellular::kModemStateUnknown;
+ }
+}
+
+CellularCapabilityClassic::CellularCapabilityClassic(
+ Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info)
+ : CellularCapability(cellular, proxy_factory, modem_info),
+ weak_ptr_factory_(this) {
+ // This class is currently instantiated only for Gobi modems so setup the
+ // supported carriers list appropriately and expose it over RPC.
+ cellular->set_supported_carriers({kCarrierGenericUMTS,
+ kCarrierSprint,
+ kCarrierVerizon});
+}
+
+CellularCapabilityClassic::~CellularCapabilityClassic() {}
+
+void CellularCapabilityClassic::InitProxies() {
+ SLOG(this, 2) << __func__;
+ proxy_.reset(proxy_factory()->CreateModemProxy(
+ cellular()->dbus_path(), cellular()->dbus_owner()));
+ simple_proxy_.reset(proxy_factory()->CreateModemSimpleProxy(
+ cellular()->dbus_path(), cellular()->dbus_owner()));
+ proxy_->set_state_changed_callback(
+ Bind(&CellularCapabilityClassic::OnModemStateChangedSignal,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CellularCapabilityClassic::ReleaseProxies() {
+ SLOG(this, 2) << __func__;
+ proxy_.reset();
+ simple_proxy_.reset();
+ gobi_proxy_.reset();
+}
+
+bool CellularCapabilityClassic::AreProxiesInitialized() const {
+ return (proxy_.get() && simple_proxy_.get() && gobi_proxy_.get());
+}
+
+void CellularCapabilityClassic::FinishEnable(const ResultCallback &callback) {
+ // Normally, running the callback is the last thing done in a method.
+ // In this case, we do it first, because we want to make sure that
+ // the device is marked as Enabled before the registration state is
+ // handled. See comment in Cellular::HandleNewRegistrationState.
+ callback.Run(Error());
+ GetRegistrationState();
+ GetSignalQuality();
+ // We expect the modem to start scanning after it has been enabled.
+ // Change this if this behavior is no longer the case in the future.
+ modem_info()->metrics()->NotifyDeviceEnableFinished(
+ cellular()->interface_index());
+ modem_info()->metrics()->NotifyDeviceScanStarted(
+ cellular()->interface_index());
+}
+
+void CellularCapabilityClassic::FinishDisable(const ResultCallback &callback) {
+ modem_info()->metrics()->NotifyDeviceDisableFinished(
+ cellular()->interface_index());
+ ReleaseProxies();
+ callback.Run(Error());
+}
+
+void CellularCapabilityClassic::RunNextStep(CellularTaskList *tasks) {
+ CHECK(!tasks->empty());
+ SLOG(this, 2) << __func__ << ": " << tasks->size() << " remaining tasks";
+ Closure task = (*tasks)[0];
+ tasks->erase(tasks->begin());
+ cellular()->dispatcher()->PostTask(task);
+}
+
+void CellularCapabilityClassic::StepCompletedCallback(
+ const ResultCallback &callback,
+ bool ignore_error,
+ CellularTaskList *tasks,
+ const Error &error) {
+ if ((ignore_error || error.IsSuccess()) && !tasks->empty()) {
+ RunNextStep(tasks);
+ return;
+ }
+ delete tasks;
+ if (!callback.is_null())
+ callback.Run(error);
+}
+
+// always called from an async context
+void CellularCapabilityClassic::EnableModem(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ CHECK(!callback.is_null());
+ Error error;
+ modem_info()->metrics()->NotifyDeviceEnableStarted(
+ cellular()->interface_index());
+ proxy_->Enable(true, &error, callback, kTimeoutEnable);
+ if (error.IsFailure())
+ callback.Run(error);
+}
+
+// always called from an async context
+void CellularCapabilityClassic::DisableModem(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ CHECK(!callback.is_null());
+ Error error;
+ modem_info()->metrics()->NotifyDeviceDisableStarted(
+ cellular()->interface_index());
+ proxy_->Enable(false, &error, callback, kTimeoutEnable);
+ if (error.IsFailure())
+ callback.Run(error);
+}
+
+// always called from an async context
+void CellularCapabilityClassic::GetModemStatus(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ CHECK(!callback.is_null());
+ DBusPropertyMapCallback cb = Bind(
+ &CellularCapabilityClassic::OnGetModemStatusReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ Error error;
+ simple_proxy_->GetModemStatus(&error, cb, kTimeoutDefault);
+ if (error.IsFailure())
+ callback.Run(error);
+}
+
+// always called from an async context
+void CellularCapabilityClassic::GetModemInfo(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ CHECK(!callback.is_null());
+ ModemInfoCallback cb = Bind(&CellularCapabilityClassic::OnGetModemInfoReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ Error error;
+ proxy_->GetModemInfo(&error, cb, kTimeoutDefault);
+ if (error.IsFailure())
+ callback.Run(error);
+}
+
+void CellularCapabilityClassic::StopModem(Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+
+ CellularTaskList *tasks = new CellularTaskList();
+ ResultCallback cb =
+ Bind(&CellularCapabilityClassic::StepCompletedCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
+ ResultCallback cb_ignore_error =
+ Bind(&CellularCapabilityClassic::StepCompletedCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
+ // TODO(ers): We can skip the call to Disconnect if the modem has
+ // told us that the modem state is Disabled or Registered.
+ tasks->push_back(Bind(&CellularCapabilityClassic::Disconnect,
+ weak_ptr_factory_.GetWeakPtr(), nullptr,
+ cb_ignore_error));
+ // TODO(ers): We can skip the call to Disable if the modem has
+ // told us that the modem state is Disabled.
+ tasks->push_back(Bind(&CellularCapabilityClassic::DisableModem,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ tasks->push_back(Bind(&CellularCapabilityClassic::FinishDisable,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+
+ RunNextStep(tasks);
+}
+
+void CellularCapabilityClassic::Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ simple_proxy_->Connect(properties, error, callback, kTimeoutConnect);
+}
+
+void CellularCapabilityClassic::Disconnect(Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ if (proxy_.get())
+ proxy_->Disconnect(error, callback, kTimeoutDisconnect);
+ else
+ LOG(ERROR) << "No proxy found in disconnect.";
+}
+
+void CellularCapabilityClassic::SetCarrier(const string &carrier,
+ Error *error,
+ const ResultCallback &callback) {
+ LOG(INFO) << __func__ << "(" << carrier << ")";
+ if (!gobi_proxy_.get()) {
+ gobi_proxy_.reset(proxy_factory()->CreateModemGobiProxy(
+ cellular()->dbus_path(), cellular()->dbus_owner()));
+ }
+ CHECK(error);
+ gobi_proxy_->SetCarrier(carrier, error, callback,
+ kTimeoutSetCarrierMilliseconds);
+}
+
+void CellularCapabilityClassic::OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties) {
+ SLOG(this, 2) << __func__;
+ bool enabled;
+ // This solves a bootstrapping problem: If the modem is not yet
+ // enabled, there are no proxy objects associated with the capability
+ // object, so modem signals like StateChanged aren't seen. By monitoring
+ // changes to the Enabled property via the ModemManager, we're able to
+ // get the initialization process started, which will result in the
+ // creation of the proxy objects.
+ //
+ // We handle all state changes to ENABLED from a disabled state (including,
+ // UNKNOWN) through Cellular::OnModemStateChanged. This will try to enable
+ // the device regardless of whether it has been registered with the Manager.
+ //
+ // All other state changes are handled from OnModemStateChangedSignal.
+ if (DBusProperties::GetBool(changed_properties,
+ kModemPropertyEnabled, &enabled)) {
+ SLOG(this, 2) << "Property \"Enabled\" changed: " << enabled;
+ Cellular::ModemState prev_modem_state = cellular()->modem_state();
+ if (!Cellular::IsEnabledModemState(prev_modem_state)) {
+ cellular()->OnModemStateChanged(
+ enabled ? Cellular::kModemStateEnabled :
+ Cellular::kModemStateDisabled);
+ }
+ }
+}
+
+void CellularCapabilityClassic::OnGetModemStatusReply(
+ const ResultCallback &callback,
+ const DBusPropertiesMap &props,
+ const Error &error) {
+ string prop_value;
+ SLOG(this, 2) << __func__ << " " << props.size() << " props. error "
+ << error;
+ if (error.IsSuccess()) {
+ if (DBusProperties::GetString(props, "carrier", &prop_value)) {
+ cellular()->set_carrier(prop_value);
+ cellular()->home_provider_info()->UpdateOperatorName(prop_value);
+ }
+ if (DBusProperties::GetString(props, "meid", &prop_value)) {
+ cellular()->set_meid(prop_value);
+ }
+ if (DBusProperties::GetString(props, "imei", &prop_value)) {
+ cellular()->set_imei(prop_value);
+ }
+ if (DBusProperties::GetString(props, kModemPropertyIMSI, &prop_value)) {
+ cellular()->set_imsi(prop_value);
+ cellular()->home_provider_info()->UpdateIMSI(prop_value);
+ // We do not currently obtain the IMSI OTA at all. Provide the IMSI from
+ // the SIM to the serving operator as well to aid in MVNO identification.
+ cellular()->serving_operator_info()->UpdateIMSI(prop_value);
+ }
+ if (DBusProperties::GetString(props, "esn", &prop_value)) {
+ cellular()->set_esn(prop_value);
+ }
+ if (DBusProperties::GetString(props, "mdn", &prop_value)) {
+ cellular()->set_mdn(prop_value);
+ }
+ if (DBusProperties::GetString(props, "min", &prop_value)) {
+ cellular()->set_min(prop_value);
+ }
+ if (DBusProperties::GetString(props, "firmware_revision", &prop_value)) {
+ cellular()->set_firmware_revision(prop_value);
+ }
+ UpdateStatus(props);
+ }
+ callback.Run(error);
+}
+
+void CellularCapabilityClassic::UpdateStatus(
+ const DBusPropertiesMap &properties) {
+ SLOG(this, 3) << __func__;
+}
+
+void CellularCapabilityClassic::OnGetModemInfoReply(
+ const ResultCallback &callback,
+ const ModemHardwareInfo &info,
+ const Error &error) {
+ SLOG(this, 2) << __func__ << "(" << error << ")";
+ if (error.IsSuccess()) {
+ cellular()->set_manufacturer(info._1);
+ cellular()->set_model_id(info._2);
+ cellular()->set_hardware_revision(info._3);
+ SLOG(this, 2) << __func__ << ": " << info._1 << ", " << info._2 << ", "
+ << info._3;
+ }
+ callback.Run(error);
+}
+
+void CellularCapabilityClassic::OnModemStateChangedSignal(
+ uint32_t old_state, uint32_t new_state, uint32_t reason) {
+ SLOG(this, 2) << __func__ << "(" << old_state << ", " << new_state << ", "
+ << reason << ")";
+ cellular()->OnModemStateChanged(ConvertClassicToModemState(new_state));
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_classic.h b/cellular/cellular_capability_classic.h
new file mode 100644
index 0000000..4e32a54
--- /dev/null
+++ b/cellular/cellular_capability_classic.h
@@ -0,0 +1,170 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_CAPABILITY_CLASSIC_H_
+#define SHILL_CELLULAR_CELLULAR_CAPABILITY_CLASSIC_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/callback.h>
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_capability.h"
+#include "shill/cellular/modem_proxy_interface.h"
+#include "shill/cellular/modem_simple_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+
+class Cellular;
+class Error;
+class EventDispatcher;
+class ModemGobiProxyInterface;
+class ModemInfo;
+class ProxyFactory;
+
+enum ModemClassicState {
+ kModemClassicStateUnknown = 0,
+ kModemClassicStateDisabled = 10,
+ kModemClassicStateDisabling = 20,
+ kModemClassicStateEnabling = 30,
+ kModemClassicStateEnabled = 40,
+ kModemClassicStateSearching = 50,
+ kModemClassicStateRegistered = 60,
+ kModemClassicStateDisconnecting = 70,
+ kModemClassicStateConnecting = 80,
+ kModemClassicStateConnected = 90,
+};
+
+// CellularCapabilityClassic handles modems using the
+// org.chromium.ModemManager DBUS interface.
+class CellularCapabilityClassic : public CellularCapability {
+ public:
+ static const char kConnectPropertyApn[];
+ static const char kConnectPropertyApnUsername[];
+ static const char kConnectPropertyApnPassword[];
+ static const char kConnectPropertyHomeOnly[];
+ static const char kConnectPropertyPhoneNumber[];
+ static const char kModemPropertyEnabled[];
+ static const int kTimeoutSetCarrierMilliseconds;
+
+ // |cellular| is the parent Cellular device.
+ CellularCapabilityClassic(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info);
+ ~CellularCapabilityClassic() override;
+
+ // Inherited from CellularCapability.
+ void OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties) override;
+ void StopModem(Error *error, const ResultCallback &callback) override;
+ bool AreProxiesInitialized() const override;
+ void SetCarrier(const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback) override;
+ void Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback) override;
+ void Disconnect(Error *error,
+ const ResultCallback &callback) override;
+
+ protected:
+ typedef std::vector<base::Closure> CellularTaskList;
+
+ virtual void GetRegistrationState() = 0;
+
+ // The following five methods are only ever called as
+ // callbacks (from the main loop), which is why they
+ // don't take an Error * argument.
+ virtual void EnableModem(const ResultCallback &callback);
+ virtual void DisableModem(const ResultCallback &callback);
+ virtual void GetModemStatus(const ResultCallback &callback);
+ virtual void GetModemInfo(const ResultCallback &callback);
+ virtual void GetProperties(const ResultCallback &callback) = 0;
+
+ void FinishEnable(const ResultCallback &callback);
+ void FinishDisable(const ResultCallback &callback);
+ virtual void InitProxies();
+ virtual void ReleaseProxies();
+
+ // Default implementation is no-op.
+ virtual void UpdateStatus(const DBusPropertiesMap &properties);
+
+ // Runs the next task in a list.
+ // Precondition: |tasks| is not empty.
+ void RunNextStep(CellularTaskList *tasks);
+ // StepCompletedCallback is called after a task completes.
+ // |callback| is the original callback that needs to be invoked when all of
+ // the tasks complete or if there is a failure. |ignore_error| will be set
+ // to true if the next task should be run regardless of the result of the
+ // just-completed task. |tasks| is the list of tasks remaining. |error| is
+ // the result of the just-completed task.
+ void StepCompletedCallback(const ResultCallback &callback,
+ bool ignore_error,
+ CellularTaskList *tasks,
+ const Error &error);
+
+ std::unique_ptr<ModemSimpleProxyInterface> simple_proxy_;
+
+ private:
+ friend class CellularTest;
+ friend class CellularCapabilityCDMATest;
+ friend class CellularCapabilityTest;
+ friend class CellularCapabilityGSMTest;
+ FRIEND_TEST(CellularCapabilityGSMTest, SetProxy);
+ FRIEND_TEST(CellularCapabilityGSMTest, SetStorageIdentifier);
+ FRIEND_TEST(CellularCapabilityGSMTest, UpdateStatus);
+ FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
+ FRIEND_TEST(CellularCapabilityTest, EnableModemFail);
+ FRIEND_TEST(CellularCapabilityTest, EnableModemSucceed);
+ FRIEND_TEST(CellularCapabilityTest, FinishEnable);
+ FRIEND_TEST(CellularCapabilityTest, GetModemInfo);
+ FRIEND_TEST(CellularCapabilityTest, GetModemStatus);
+ FRIEND_TEST(CellularCapabilityTest, TryApns);
+ FRIEND_TEST(CellularServiceTest, FriendlyName);
+ FRIEND_TEST(CellularTest, StartCDMARegister);
+ FRIEND_TEST(CellularTest, StartConnected);
+ FRIEND_TEST(CellularTest, StartGSMRegister);
+ FRIEND_TEST(CellularTest, StartLinked);
+ FRIEND_TEST(CellularTest, Connect);
+ FRIEND_TEST(CellularTest, ConnectFailure);
+ FRIEND_TEST(CellularTest, ConnectFailureNoService);
+ FRIEND_TEST(CellularTest, ConnectSuccessNoService);
+ FRIEND_TEST(CellularTest, Disconnect);
+ FRIEND_TEST(CellularTest, DisconnectFailure);
+ FRIEND_TEST(CellularTest, DisconnectWithCallback);
+ FRIEND_TEST(CellularTest, ModemStateChangeEnable);
+ FRIEND_TEST(CellularTest, ModemStateChangeDisable);
+
+ // Method reply and signal callbacks from Modem interface
+ void OnModemStateChangedSignal(uint32_t old_state,
+ uint32_t new_state,
+ uint32_t reason);
+ void OnGetModemInfoReply(const ResultCallback &callback,
+ const ModemHardwareInfo &info,
+ const Error &error);
+
+ // Method reply callbacks from Modem.Simple interface
+ void OnGetModemStatusReply(const ResultCallback &callback,
+ const DBusPropertiesMap &props,
+ const Error &error);
+
+ Cellular *cellular_;
+ base::WeakPtrFactory<CellularCapabilityClassic> weak_ptr_factory_;
+ std::unique_ptr<ModemProxyInterface> proxy_;
+ std::unique_ptr<ModemGobiProxyInterface> gobi_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(CellularCapabilityClassic);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_CAPABILITY_CLASSIC_H_
diff --git a/cellular/cellular_capability_classic_unittest.cc b/cellular/cellular_capability_classic_unittest.cc
new file mode 100644
index 0000000..9752c77
--- /dev/null
+++ b/cellular/cellular_capability_classic_unittest.cc
@@ -0,0 +1,519 @@
+// Copyright (c) 2012 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/cellular_capability_gsm.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/service_constants.h>
+#include <mm/mm-modem.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/mock_modem_cdma_proxy.h"
+#include "shill/cellular/mock_modem_gobi_proxy.h"
+#include "shill/cellular/mock_modem_gsm_card_proxy.h"
+#include "shill/cellular/mock_modem_gsm_network_proxy.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/cellular/mock_modem_proxy.h"
+#include "shill/cellular/mock_modem_simple_proxy.h"
+#include "shill/error.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_adaptors.h"
+#include "shill/mock_profile.h"
+#include "shill/net/mock_rtnl_handler.h"
+#include "shill/proxy_factory.h"
+#include "shill/testing.h"
+
+using base::Bind;
+using base::Unretained;
+using std::string;
+using testing::InSequence;
+using testing::NiceMock;
+using testing::_;
+
+namespace shill {
+
+class CellularCapabilityTest : public testing::Test {
+ public:
+ CellularCapabilityTest()
+ : modem_info_(nullptr, &dispatcher_, nullptr, nullptr, nullptr),
+ create_gsm_card_proxy_from_factory_(false),
+ proxy_(new MockModemProxy()),
+ simple_proxy_(new MockModemSimpleProxy()),
+ cdma_proxy_(new MockModemCDMAProxy()),
+ gsm_card_proxy_(new MockModemGSMCardProxy()),
+ gsm_network_proxy_(new MockModemGSMNetworkProxy()),
+ gobi_proxy_(new MockModemGobiProxy()),
+ proxy_factory_(this),
+ capability_(nullptr),
+ device_adaptor_(nullptr),
+ cellular_(new Cellular(&modem_info_,
+ "",
+ "",
+ 0,
+ Cellular::kTypeGSM,
+ "",
+ "",
+ "",
+ &proxy_factory_)) {
+ modem_info_.metrics()->RegisterDevice(cellular_->interface_index(),
+ Technology::kCellular);
+ }
+
+ virtual ~CellularCapabilityTest() {
+ cellular_->service_ = nullptr;
+ capability_ = nullptr;
+ device_adaptor_ = nullptr;
+ }
+
+ virtual void SetUp() {
+ static_cast<Device *>(cellular_)->rtnl_handler_ = &rtnl_handler_;
+
+ capability_ = dynamic_cast<CellularCapabilityClassic *>(
+ cellular_->capability_.get());
+ device_adaptor_ =
+ dynamic_cast<DeviceMockAdaptor*>(cellular_->adaptor());
+ ASSERT_NE(nullptr, device_adaptor_);;
+ }
+
+ virtual void TearDown() {
+ capability_->proxy_factory_ = nullptr;
+ }
+
+ void CreateService() {
+ // The following constants are never directly accessed by the tests.
+ const char kStorageIdentifier[] = "default_test_storage_id";
+ const char kFriendlyServiceName[] = "default_test_service_name";
+ const char kOperatorCode[] = "10010";
+ const char kOperatorName[] = "default_test_operator_name";
+ const char kOperatorCountry[] = "us";
+
+ // Simulate all the side-effects of Cellular::CreateService
+ auto service = new CellularService(&modem_info_, cellular_);
+ service->SetStorageIdentifier(kStorageIdentifier);
+ service->SetFriendlyName(kFriendlyServiceName);
+
+ Stringmap serving_operator;
+ serving_operator[kOperatorCodeKey] = kOperatorCode;
+ serving_operator[kOperatorNameKey] = kOperatorName;
+ serving_operator[kOperatorCountryKey] = kOperatorCountry;
+
+ service->set_serving_operator(serving_operator);
+ cellular_->set_home_provider(serving_operator);
+ cellular_->service_ = service;
+ }
+
+ CellularCapabilityGSM *GetGsmCapability() {
+ return dynamic_cast<CellularCapabilityGSM *>(cellular_->capability_.get());
+ }
+
+ void ReleaseCapabilityProxies() {
+ capability_->ReleaseProxies();
+ }
+
+ void InvokeEnable(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeEnableFail(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error(Error::kOperationFailed));
+ }
+ void InvokeDisconnect(Error *error, const ResultCallback &callback,
+ int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeDisconnectFail(Error *error, const ResultCallback &callback,
+ int timeout) {
+ callback.Run(Error(Error::kOperationFailed));
+ }
+ void InvokeGetModemStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout) {
+ DBusPropertiesMap props;
+ props["carrier"].writer().append_string(kTestCarrier);
+ props["unknown-property"].writer().append_string("irrelevant-value");
+ callback.Run(props, Error());
+ }
+ void InvokeGetModemInfo(Error *error, const ModemInfoCallback &callback,
+ int timeout) {
+ ModemHardwareInfo info;
+ info._1 = kManufacturer;
+ info._2 = kModelID;
+ info._3 = kHWRev;
+ callback.Run(info, Error());
+ }
+ void InvokeSetCarrier(const string &carrier, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+
+ MOCK_METHOD1(TestCallback, void(const Error &error));
+
+ protected:
+ static const char kTestMobileProviderDBPath[];
+ static const char kTestCarrier[];
+ static const char kManufacturer[];
+ static const char kModelID[];
+ static const char kHWRev[];
+
+ class TestProxyFactory : public ProxyFactory {
+ public:
+ explicit TestProxyFactory(CellularCapabilityTest *test) : test_(test) {}
+
+ virtual ModemProxyInterface *CreateModemProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ return test_->proxy_.release();
+ }
+
+ virtual ModemSimpleProxyInterface *CreateModemSimpleProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ return test_->simple_proxy_.release();
+ }
+
+ virtual ModemCDMAProxyInterface *CreateModemCDMAProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ return test_->cdma_proxy_.release();
+ }
+
+ virtual ModemGSMCardProxyInterface *CreateModemGSMCardProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ // TODO(benchan): This code conditionally returns a nullptr to avoid
+ // CellularCapabilityGSM::InitProperties (and thus
+ // CellularCapabilityGSM::GetIMSI) from being called during the
+ // construction. Remove this workaround after refactoring the tests.
+ return test_->create_gsm_card_proxy_from_factory_ ?
+ test_->gsm_card_proxy_.release() : nullptr;
+ }
+
+ virtual ModemGSMNetworkProxyInterface *CreateModemGSMNetworkProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ return test_->gsm_network_proxy_.release();
+ }
+
+ virtual ModemGobiProxyInterface *CreateModemGobiProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ return test_->gobi_proxy_.release();
+ }
+
+ private:
+ CellularCapabilityTest *test_;
+ };
+
+ void SetProxy() {
+ capability_->proxy_.reset(proxy_.release());
+ }
+
+ void SetSimpleProxy() {
+ capability_->simple_proxy_.reset(simple_proxy_.release());
+ }
+
+ void SetGSMNetworkProxy() {
+ CellularCapabilityGSM *gsm_capability =
+ dynamic_cast<CellularCapabilityGSM *>(cellular_->capability_.get());
+ gsm_capability->network_proxy_.reset(gsm_network_proxy_.release());
+ }
+
+ void SetCellularType(Cellular::Type type) {
+ cellular_->InitCapability(type);
+ capability_ = dynamic_cast<CellularCapabilityClassic *>(
+ cellular_->capability_.get());
+ }
+
+ void AllowCreateGSMCardProxyFromFactory() {
+ create_gsm_card_proxy_from_factory_ = true;
+ }
+
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ MockRTNLHandler rtnl_handler_;
+ bool create_gsm_card_proxy_from_factory_;
+ std::unique_ptr<MockModemProxy> proxy_;
+ std::unique_ptr<MockModemSimpleProxy> simple_proxy_;
+ std::unique_ptr<MockModemCDMAProxy> cdma_proxy_;
+ std::unique_ptr<MockModemGSMCardProxy> gsm_card_proxy_;
+ std::unique_ptr<MockModemGSMNetworkProxy> gsm_network_proxy_;
+ std::unique_ptr<MockModemGobiProxy> gobi_proxy_;
+ TestProxyFactory proxy_factory_;
+ CellularCapabilityClassic *capability_; // Owned by |cellular_|.
+ DeviceMockAdaptor *device_adaptor_; // Owned by |cellular_|.
+ CellularRefPtr cellular_;
+};
+
+const char CellularCapabilityTest::kTestMobileProviderDBPath[] =
+ "provider_db_unittest.bfd";
+const char CellularCapabilityTest::kTestCarrier[] = "The Cellular Carrier";
+const char CellularCapabilityTest::kManufacturer[] = "Company";
+const char CellularCapabilityTest::kModelID[] = "Gobi 2000";
+const char CellularCapabilityTest::kHWRev[] = "A00B1234";
+
+TEST_F(CellularCapabilityTest, GetModemStatus) {
+ SetCellularType(Cellular::kTypeCDMA);
+ EXPECT_CALL(*simple_proxy_,
+ GetModemStatus(_, _, CellularCapability::kTimeoutDefault)).
+ WillOnce(Invoke(this, &CellularCapabilityTest::InvokeGetModemStatus));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetSimpleProxy();
+ ResultCallback callback =
+ Bind(&CellularCapabilityTest::TestCallback, Unretained(this));
+ capability_->GetModemStatus(callback);
+ EXPECT_EQ(kTestCarrier, cellular_->carrier());
+}
+
+TEST_F(CellularCapabilityTest, GetModemInfo) {
+ EXPECT_CALL(*proxy_, GetModemInfo(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeGetModemInfo));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetProxy();
+ ResultCallback callback =
+ Bind(&CellularCapabilityTest::TestCallback, Unretained(this));
+ capability_->GetModemInfo(callback);
+ EXPECT_EQ(kManufacturer, cellular_->manufacturer());
+ EXPECT_EQ(kModelID, cellular_->model_id());
+ EXPECT_EQ(kHWRev, cellular_->hardware_revision());
+}
+
+TEST_F(CellularCapabilityTest, EnableModemSucceed) {
+ EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeEnable));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ ResultCallback callback =
+ Bind(&CellularCapabilityTest::TestCallback, Unretained(this));
+ SetProxy();
+ capability_->EnableModem(callback);
+}
+
+TEST_F(CellularCapabilityTest, EnableModemFail) {
+ EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeEnableFail));
+ EXPECT_CALL(*this, TestCallback(IsFailure()));
+ ResultCallback callback =
+ Bind(&CellularCapabilityTest::TestCallback, Unretained(this));
+ SetProxy();
+ capability_->EnableModem(callback);
+}
+
+TEST_F(CellularCapabilityTest, FinishEnable) {
+ EXPECT_CALL(*gsm_network_proxy_,
+ GetRegistrationInfo(nullptr, _,
+ CellularCapability::kTimeoutDefault));
+ EXPECT_CALL(
+ *gsm_network_proxy_,
+ GetSignalQuality(nullptr, _, CellularCapability::kTimeoutDefault));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetGSMNetworkProxy();
+ capability_->FinishEnable(
+ Bind(&CellularCapabilityTest::TestCallback, Unretained(this)));
+}
+
+TEST_F(CellularCapabilityTest, UnsupportedOperation) {
+ Error error;
+ EXPECT_CALL(*this, TestCallback(IsSuccess())).Times(0);
+ capability_->CellularCapability::Reset(
+ &error,
+ Bind(&CellularCapabilityTest::TestCallback, Unretained(this)));
+ EXPECT_TRUE(error.IsFailure());
+ EXPECT_EQ(Error::kNotSupported, error.type());
+}
+
+TEST_F(CellularCapabilityTest, AllowRoaming) {
+ EXPECT_FALSE(cellular_->GetAllowRoaming(nullptr));
+ cellular_->SetAllowRoaming(false, nullptr);
+ EXPECT_FALSE(cellular_->GetAllowRoaming(nullptr));
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*device_adaptor_,
+ EmitBoolChanged(kCellularAllowRoamingProperty, true));
+ EXPECT_CALL(*device_adaptor_,
+ EmitBoolChanged(kCellularAllowRoamingProperty, false));
+ }
+
+ cellular_->state_ = Cellular::kStateConnected;
+ dynamic_cast<CellularCapabilityGSM *>(capability_)->registration_state_ =
+ MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
+ cellular_->SetAllowRoaming(true, nullptr);
+ EXPECT_TRUE(cellular_->GetAllowRoaming(nullptr));
+ EXPECT_EQ(Cellular::kStateConnected, cellular_->state_);
+
+ EXPECT_CALL(*proxy_, Disconnect(_, _, CellularCapability::kTimeoutDisconnect))
+ .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeDisconnect));
+ SetProxy();
+ cellular_->state_ = Cellular::kStateConnected;
+ cellular_->SetAllowRoaming(false, nullptr);
+ EXPECT_FALSE(cellular_->GetAllowRoaming(nullptr));
+ EXPECT_EQ(Cellular::kStateRegistered, cellular_->state_);
+}
+
+TEST_F(CellularCapabilityTest, SetCarrier) {
+ static const char kCarrier[] = "Generic UMTS";
+ EXPECT_CALL(
+ *gobi_proxy_,
+ SetCarrier(kCarrier, _, _,
+ CellularCapabilityClassic::kTimeoutSetCarrierMilliseconds))
+ .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeSetCarrier));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ Error error;
+ capability_->SetCarrier(kCarrier, &error,
+ Bind(&CellularCapabilityTest::TestCallback,
+ Unretained(this)));
+ EXPECT_TRUE(error.IsSuccess());
+}
+
+MATCHER_P(HasApn, apn, "") {
+ DBusPropertiesMap::const_iterator it = arg.find(kApnProperty);
+ return it != arg.end() && apn == it->second.reader().get_string();
+}
+
+MATCHER(HasNoApn, "") {
+ return arg.find(kApnProperty) == arg.end();
+}
+
+TEST_F(CellularCapabilityTest, TryApns) {
+ static const string kLastGoodApn("remembered.apn");
+ static const string kLastGoodUsername("remembered.user");
+ static const string kSuppliedApn("my.apn");
+ static const string kTmobileApn1("epc.tmobile.com");
+ static const string kTmobileApn2("wap.voicestream.com");
+ static const string kTmobileApn3("internet2.voicestream.com");
+ static const string kTmobileApn4("internet3.voicestream.com");
+ const Stringmaps kDatabaseApnList {{{ kApnProperty, kTmobileApn1 }},
+ {{ kApnProperty, kTmobileApn2 }},
+ {{ kApnProperty, kTmobileApn3 }},
+ {{ kApnProperty, kTmobileApn4 }}};
+
+
+ CreateService();
+ // Supply the database APNs to |cellular_| object.
+ cellular_->set_apn_list(kDatabaseApnList);
+ ProfileRefPtr profile(new NiceMock<MockProfile>(
+ modem_info_.control_interface(), modem_info_.metrics(),
+ modem_info_.manager()));
+ cellular_->service()->set_profile(profile);
+
+ Error error;
+ Stringmap apn_info;
+ DBusPropertiesMap props;
+ CellularCapabilityGSM *gsm_capability = GetGsmCapability();
+
+ apn_info[kApnProperty] = kLastGoodApn;
+ apn_info[kApnUsernameProperty] = kLastGoodUsername;
+ cellular_->service()->SetLastGoodApn(apn_info);
+ props.clear();
+ EXPECT_TRUE(props.find(kApnProperty) == props.end());
+ gsm_capability->SetupConnectProperties(&props);
+ // We expect the list to contain the last good APN, plus
+ // the 4 APNs from the mobile provider info database.
+ EXPECT_EQ(5, gsm_capability->apn_try_list_.size());
+ EXPECT_FALSE(props.find(kApnProperty) == props.end());
+ EXPECT_EQ(kLastGoodApn, props[kApnProperty].reader().get_string());
+ EXPECT_FALSE(props.find(kApnUsernameProperty) == props.end());
+ EXPECT_EQ(kLastGoodUsername,
+ props[kApnUsernameProperty].reader().get_string());
+
+ apn_info.clear();
+ props.clear();
+ apn_info[kApnProperty] = kSuppliedApn;
+ // Setting the APN has the side effect of clearing the LastGoodApn,
+ // so the try list will have 5 elements, with the first one being
+ // the supplied APN.
+ cellular_->service()->SetApn(apn_info, &error);
+ EXPECT_TRUE(props.find(kApnProperty) == props.end());
+ gsm_capability->SetupConnectProperties(&props);
+ EXPECT_EQ(5, gsm_capability->apn_try_list_.size());
+ EXPECT_FALSE(props.find(kApnProperty) == props.end());
+ EXPECT_EQ(kSuppliedApn, props[kApnProperty].reader().get_string());
+
+ apn_info.clear();
+ props.clear();
+ apn_info[kApnProperty] = kLastGoodApn;
+ apn_info[kApnUsernameProperty] = kLastGoodUsername;
+ // Now when LastGoodAPN is set, it will be the one selected.
+ cellular_->service()->SetLastGoodApn(apn_info);
+ EXPECT_TRUE(props.find(kApnProperty) == props.end());
+ gsm_capability->SetupConnectProperties(&props);
+ // We expect the list to contain the last good APN, plus
+ // the user-supplied APN, plus the 4 APNs from the mobile
+ // provider info database.
+ EXPECT_EQ(6, gsm_capability->apn_try_list_.size());
+ EXPECT_FALSE(props.find(kApnProperty) == props.end());
+ EXPECT_EQ(kLastGoodApn, props[kApnProperty].reader().get_string());
+
+ // Now try all the given APNs.
+ using testing::InSequence;
+ {
+ InSequence dummy;
+ EXPECT_CALL(*simple_proxy_, Connect(HasApn(kLastGoodApn), _, _, _));
+ EXPECT_CALL(*simple_proxy_, Connect(HasApn(kSuppliedApn), _, _, _));
+ EXPECT_CALL(*simple_proxy_, Connect(HasApn(kTmobileApn1), _, _, _));
+ EXPECT_CALL(*simple_proxy_, Connect(HasApn(kTmobileApn2), _, _, _));
+ EXPECT_CALL(*simple_proxy_, Connect(HasApn(kTmobileApn3), _, _, _));
+ EXPECT_CALL(*simple_proxy_, Connect(HasApn(kTmobileApn4), _, _, _));
+ EXPECT_CALL(*simple_proxy_, Connect(HasNoApn(), _, _, _));
+ }
+ SetSimpleProxy();
+ gsm_capability->Connect(props, &error, ResultCallback());
+ Error cerror(Error::kInvalidApn);
+ gsm_capability->OnConnectReply(ResultCallback(), cerror);
+ EXPECT_EQ(5, gsm_capability->apn_try_list_.size());
+ gsm_capability->OnConnectReply(ResultCallback(), cerror);
+ EXPECT_EQ(4, gsm_capability->apn_try_list_.size());
+ gsm_capability->OnConnectReply(ResultCallback(), cerror);
+ EXPECT_EQ(3, gsm_capability->apn_try_list_.size());
+ gsm_capability->OnConnectReply(ResultCallback(), cerror);
+ EXPECT_EQ(2, gsm_capability->apn_try_list_.size());
+ gsm_capability->OnConnectReply(ResultCallback(), cerror);
+ EXPECT_EQ(1, gsm_capability->apn_try_list_.size());
+ gsm_capability->OnConnectReply(ResultCallback(), cerror);
+ EXPECT_EQ(0, gsm_capability->apn_try_list_.size());
+}
+
+TEST_F(CellularCapabilityTest, StopModemDisconnectSuccess) {
+ EXPECT_CALL(*proxy_, Disconnect(_, _, CellularCapability::kTimeoutDisconnect))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityTest::InvokeDisconnect));
+ EXPECT_CALL(*proxy_, Enable(_, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityTest::InvokeEnable));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetProxy();
+
+ Error error;
+ capability_->StopModem(
+ &error, Bind(&CellularCapabilityTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularCapabilityTest, StopModemDisconnectFail) {
+ EXPECT_CALL(*proxy_, Disconnect(_, _, CellularCapability::kTimeoutDisconnect))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityTest::InvokeDisconnectFail));
+ EXPECT_CALL(*proxy_, Enable(_, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityTest::InvokeEnable));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetProxy();
+
+ Error error;
+ capability_->StopModem(
+ &error, Bind(&CellularCapabilityTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularCapabilityTest, DisconnectNoProxy) {
+ Error error;
+ ResultCallback disconnect_callback;
+ EXPECT_CALL(*proxy_, Disconnect(_, _, CellularCapability::kTimeoutDisconnect))
+ .Times(0);
+ ReleaseCapabilityProxies();
+ capability_->Disconnect(&error, disconnect_callback);
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_gsm.cc b/cellular/cellular_capability_gsm.cc
new file mode 100644
index 0000000..7bbd66c
--- /dev/null
+++ b/cellular/cellular_capability_gsm.cc
@@ -0,0 +1,798 @@
+// Copyright (c) 2012 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/cellular_capability_gsm.h"
+
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/stl_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <mm/mm-modem.h>
+
+#include "shill/adaptor_interfaces.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+#include "shill/property_accessor.h"
+#include "shill/proxy_factory.h"
+
+using base::Bind;
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularCapabilityGSM *c) {
+ return c->cellular()->GetRpcIdentifier();
+}
+}
+
+// static
+const char CellularCapabilityGSM::kNetworkPropertyAccessTechnology[] =
+ "access-tech";
+const char CellularCapabilityGSM::kNetworkPropertyID[] = "operator-num";
+const char CellularCapabilityGSM::kNetworkPropertyLongName[] = "operator-long";
+const char CellularCapabilityGSM::kNetworkPropertyShortName[] =
+ "operator-short";
+const char CellularCapabilityGSM::kNetworkPropertyStatus[] = "status";
+const char CellularCapabilityGSM::kPhoneNumber[] = "*99#";
+const char CellularCapabilityGSM::kPropertyAccessTechnology[] =
+ "AccessTechnology";
+const char CellularCapabilityGSM::kPropertyEnabledFacilityLocks[] =
+ "EnabledFacilityLocks";
+const char CellularCapabilityGSM::kPropertyUnlockRequired[] = "UnlockRequired";
+const char CellularCapabilityGSM::kPropertyUnlockRetries[] = "UnlockRetries";
+
+const int CellularCapabilityGSM::kGetIMSIRetryLimit = 40;
+const int64_t CellularCapabilityGSM::kGetIMSIRetryDelayMilliseconds = 500;
+
+
+CellularCapabilityGSM::CellularCapabilityGSM(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info)
+ : CellularCapabilityClassic(cellular, proxy_factory, modem_info),
+ weak_ptr_factory_(this),
+ mobile_operator_info_(new MobileOperatorInfo(cellular->dispatcher(),
+ "ParseScanResult")),
+ registration_state_(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN),
+ access_technology_(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN),
+ home_provider_info_(nullptr),
+ get_imsi_retries_(0),
+ get_imsi_retry_delay_milliseconds_(kGetIMSIRetryDelayMilliseconds) {
+ SLOG(this, 2) << "Cellular capability constructed: GSM";
+ mobile_operator_info_->Init();
+ HelpRegisterConstDerivedKeyValueStore(
+ kSIMLockStatusProperty, &CellularCapabilityGSM::SimLockStatusToProperty);
+ this->cellular()->set_scanning_supported(true);
+
+ // TODO(benchan): This is a hack to initialize the GSM card proxy for GetIMSI
+ // before InitProxies is called. There are side-effects of calling InitProxies
+ // before the device is enabled. It's better to refactor InitProxies such that
+ // proxies can be created when the cellular device/capability is constructed,
+ // but callbacks for DBus signal updates are not set up until the device is
+ // enabled.
+ card_proxy_.reset(
+ proxy_factory->CreateModemGSMCardProxy(cellular->dbus_path(),
+ cellular->dbus_owner()));
+ // TODO(benchan): To allow unit testing using a mock proxy without further
+ // complicating the code, the test proxy factory is set up to return a nullptr
+ // pointer when CellularCapabilityGSM is constructed. Refactor the code to
+ // avoid this hack.
+ if (card_proxy_.get())
+ InitProperties();
+}
+
+CellularCapabilityGSM::~CellularCapabilityGSM() {}
+
+string CellularCapabilityGSM::GetTypeString() const {
+ return kTechnologyFamilyGsm;
+}
+
+KeyValueStore CellularCapabilityGSM::SimLockStatusToProperty(Error */*error*/) {
+ KeyValueStore status;
+ status.SetBool(kSIMLockEnabledProperty, sim_lock_status_.enabled);
+ status.SetString(kSIMLockTypeProperty, sim_lock_status_.lock_type);
+ status.SetUint(kSIMLockRetriesLeftProperty, sim_lock_status_.retries_left);
+ return status;
+}
+
+void CellularCapabilityGSM::HelpRegisterConstDerivedKeyValueStore(
+ const string &name,
+ KeyValueStore(CellularCapabilityGSM::*get)(Error *error)) {
+ cellular()->mutable_store()->RegisterDerivedKeyValueStore(
+ name,
+ KeyValueStoreAccessor(
+ new CustomAccessor<CellularCapabilityGSM, KeyValueStore>(
+ this, get, nullptr)));
+}
+
+void CellularCapabilityGSM::InitProxies() {
+ CellularCapabilityClassic::InitProxies();
+ // TODO(benchan): Remove this check after refactoring the proxy
+ // initialization.
+ if (!card_proxy_.get()) {
+ card_proxy_.reset(
+ proxy_factory()->CreateModemGSMCardProxy(cellular()->dbus_path(),
+ cellular()->dbus_owner()));
+ }
+ network_proxy_.reset(
+ proxy_factory()->CreateModemGSMNetworkProxy(cellular()->dbus_path(),
+ cellular()->dbus_owner()));
+ network_proxy_->set_signal_quality_callback(
+ Bind(&CellularCapabilityGSM::OnSignalQualitySignal,
+ weak_ptr_factory_.GetWeakPtr()));
+ network_proxy_->set_network_mode_callback(
+ Bind(&CellularCapabilityGSM::OnNetworkModeSignal,
+ weak_ptr_factory_.GetWeakPtr()));
+ network_proxy_->set_registration_info_callback(
+ Bind(&CellularCapabilityGSM::OnRegistrationInfoSignal,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CellularCapabilityGSM::InitProperties() {
+ CellularTaskList *tasks = new CellularTaskList();
+ ResultCallback cb_ignore_error =
+ Bind(&CellularCapabilityGSM::StepCompletedCallback,
+ weak_ptr_factory_.GetWeakPtr(), ResultCallback(), true, tasks);
+ // Chrome checks if a SIM is present before allowing the modem to be enabled,
+ // so shill needs to obtain IMSI, as an indicator of SIM presence, even
+ // before the device is enabled.
+ tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
+ weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
+ RunNextStep(tasks);
+}
+
+void CellularCapabilityGSM::StartModem(Error *error,
+ const ResultCallback &callback) {
+ InitProxies();
+
+ CellularTaskList *tasks = new CellularTaskList();
+ ResultCallback cb =
+ Bind(&CellularCapabilityGSM::StepCompletedCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
+ ResultCallback cb_ignore_error =
+ Bind(&CellularCapabilityGSM::StepCompletedCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
+ if (!cellular()->IsUnderlyingDeviceEnabled())
+ tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ // If we're within range of the home network, the modem will try to
+ // register once it's enabled, or may be already registered if we
+ // started out enabled.
+ if (!IsUnderlyingDeviceRegistered() &&
+ !cellular()->selected_network().empty())
+ tasks->push_back(Bind(&CellularCapabilityGSM::Register,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ tasks->push_back(Bind(&CellularCapabilityGSM::GetIMEI,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ get_imsi_retries_ = 0;
+ tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ tasks->push_back(Bind(&CellularCapabilityGSM::GetSPN,
+ weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
+ tasks->push_back(Bind(&CellularCapabilityGSM::GetMSISDN,
+ weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
+ tasks->push_back(Bind(&CellularCapabilityGSM::GetProperties,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+ tasks->push_back(Bind(&CellularCapabilityGSM::GetModemInfo,
+ weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
+ tasks->push_back(Bind(&CellularCapabilityGSM::FinishEnable,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+
+ RunNextStep(tasks);
+}
+
+bool CellularCapabilityGSM::IsUnderlyingDeviceRegistered() const {
+ switch (cellular()->modem_state()) {
+ case Cellular::kModemStateFailed:
+ case Cellular::kModemStateUnknown:
+ case Cellular::kModemStateDisabled:
+ case Cellular::kModemStateInitializing:
+ case Cellular::kModemStateLocked:
+ case Cellular::kModemStateDisabling:
+ case Cellular::kModemStateEnabling:
+ case Cellular::kModemStateEnabled:
+ return false;
+ case Cellular::kModemStateSearching:
+ case Cellular::kModemStateRegistered:
+ case Cellular::kModemStateDisconnecting:
+ case Cellular::kModemStateConnecting:
+ case Cellular::kModemStateConnected:
+ return true;
+ }
+ return false;
+}
+
+void CellularCapabilityGSM::ReleaseProxies() {
+ SLOG(this, 2) << __func__;
+ CellularCapabilityClassic::ReleaseProxies();
+ card_proxy_.reset();
+ network_proxy_.reset();
+}
+
+bool CellularCapabilityGSM::AreProxiesInitialized() const {
+ return (CellularCapabilityClassic::AreProxiesInitialized() &&
+ card_proxy_.get() && network_proxy_.get());
+}
+
+void CellularCapabilityGSM::OnServiceCreated() {
+ cellular()->service()->SetActivationState(kActivationStateActivated);
+}
+
+// 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(),
+ cellular()->apn_list().begin(),
+ cellular()->apn_list().end());
+}
+
+void CellularCapabilityGSM::SetupConnectProperties(
+ DBusPropertiesMap *properties) {
+ SetupApnTryList();
+ FillConnectPropertyMap(properties);
+}
+
+void CellularCapabilityGSM::FillConnectPropertyMap(
+ DBusPropertiesMap *properties) {
+ (*properties)[kConnectPropertyPhoneNumber].writer().append_string(
+ kPhoneNumber);
+
+ if (!AllowRoaming())
+ (*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();
+ SLOG(this, 2) << __func__ << ": Using APN " << apn_info[kApnProperty];
+ (*properties)[kConnectPropertyApn].writer().append_string(
+ apn_info[kApnProperty].c_str());
+ if (ContainsKey(apn_info, kApnUsernameProperty))
+ (*properties)[kConnectPropertyApnUsername].writer().append_string(
+ apn_info[kApnUsernameProperty].c_str());
+ if (ContainsKey(apn_info, kApnPasswordProperty))
+ (*properties)[kConnectPropertyApnPassword].writer().append_string(
+ apn_info[kApnPasswordProperty].c_str());
+ }
+}
+
+void CellularCapabilityGSM::Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ ResultCallback cb = Bind(&CellularCapabilityGSM::OnConnectReply,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
+}
+
+void CellularCapabilityGSM::OnConnectReply(const ResultCallback &callback,
+ const Error &error) {
+ CellularServiceRefPtr service = cellular()->service();
+ if (!service) {
+ // The service could have been deleted before our Connect() request
+ // completes if the modem was enabled and then quickly disabled.
+ apn_try_list_.clear();
+ } else if (error.IsFailure()) {
+ 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();
+ SLOG(this, 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()) {
+ service->SetLastGoodApn(apn_try_list_.front());
+ apn_try_list_.clear();
+ }
+ if (!callback.is_null())
+ callback.Run(error);
+}
+
+bool CellularCapabilityGSM::AllowRoaming() {
+ return cellular()->provider_requires_roaming() || allow_roaming_property();
+}
+
+// always called from an async context
+void CellularCapabilityGSM::GetIMEI(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ CHECK(!callback.is_null());
+ Error error;
+ if (cellular()->imei().empty()) {
+ GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMEIReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ card_proxy_->GetIMEI(&error, cb, kTimeoutDefault);
+ if (error.IsFailure())
+ callback.Run(error);
+ } else {
+ SLOG(this, 2) << "Already have IMEI " << cellular()->imei();
+ callback.Run(error);
+ }
+}
+
+// always called from an async context
+void CellularCapabilityGSM::GetIMSI(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ CHECK(!callback.is_null());
+ Error error;
+ if (cellular()->imsi().empty()) {
+ GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMSIReply,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ card_proxy_->GetIMSI(&error, cb, kTimeoutDefault);
+ if (error.IsFailure()) {
+ cellular()->home_provider_info()->Reset();
+ callback.Run(error);
+ }
+ } else {
+ SLOG(this, 2) << "Already have IMSI " << cellular()->imsi();
+ callback.Run(error);
+ }
+}
+
+// always called from an async context
+void CellularCapabilityGSM::GetSPN(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ CHECK(!callback.is_null());
+ Error error;
+ if (spn_.empty()) {
+ GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetSPNReply,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ card_proxy_->GetSPN(&error, cb, kTimeoutDefault);
+ if (error.IsFailure())
+ callback.Run(error);
+ } else {
+ SLOG(this, 2) << "Already have SPN " << spn_;
+ callback.Run(error);
+ }
+}
+
+// always called from an async context
+void CellularCapabilityGSM::GetMSISDN(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+ CHECK(!callback.is_null());
+ Error error;
+ string mdn = cellular()->mdn();
+ if (mdn.empty()) {
+ GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetMSISDNReply,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ card_proxy_->GetMSISDN(&error, cb, kTimeoutDefault);
+ if (error.IsFailure())
+ callback.Run(error);
+ } else {
+ SLOG(this, 2) << "Already have MSISDN " << mdn;
+ callback.Run(error);
+ }
+}
+
+void CellularCapabilityGSM::GetSignalQuality() {
+ SLOG(this, 2) << __func__;
+ SignalQualityCallback callback =
+ Bind(&CellularCapabilityGSM::OnGetSignalQualityReply,
+ weak_ptr_factory_.GetWeakPtr());
+ network_proxy_->GetSignalQuality(nullptr, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityGSM::GetRegistrationState() {
+ SLOG(this, 2) << __func__;
+ RegistrationInfoCallback callback =
+ Bind(&CellularCapabilityGSM::OnGetRegistrationInfoReply,
+ weak_ptr_factory_.GetWeakPtr());
+ network_proxy_->GetRegistrationInfo(nullptr, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityGSM::GetProperties(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__;
+
+ // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
+ uint32_t tech = network_proxy_->AccessTechnology();
+ SetAccessTechnology(tech);
+ SLOG(this, 2) << "GSM AccessTechnology: " << tech;
+
+ // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
+ uint32_t locks = card_proxy_->EnabledFacilityLocks();
+ sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
+ SLOG(this, 2) << "GSM EnabledFacilityLocks: " << locks;
+
+ callback.Run(Error());
+}
+
+// always called from an async context
+void CellularCapabilityGSM::Register(const ResultCallback &callback) {
+ SLOG(this, 2) << __func__ << " \"" << cellular()->selected_network()
+ << "\"";
+ CHECK(!callback.is_null());
+ Error error;
+ ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ network_proxy_->Register(cellular()->selected_network(), &error, cb,
+ kTimeoutRegister);
+ if (error.IsFailure())
+ callback.Run(error);
+}
+
+void CellularCapabilityGSM::RegisterOnNetwork(
+ const string &network_id,
+ Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 2) << __func__ << "(" << network_id << ")";
+ CHECK(error);
+ desired_network_ = network_id;
+ ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ network_proxy_->Register(network_id, error, cb, kTimeoutRegister);
+}
+
+void CellularCapabilityGSM::OnRegisterReply(const ResultCallback &callback,
+ const Error &error) {
+ SLOG(this, 2) << __func__ << "(" << error << ")";
+
+ if (error.IsSuccess()) {
+ cellular()->set_selected_network(desired_network_);
+ desired_network_.clear();
+ callback.Run(error);
+ return;
+ }
+ // If registration on the desired network failed,
+ // try to register on the home network.
+ if (!desired_network_.empty()) {
+ desired_network_.clear();
+ cellular()->set_selected_network("");
+ LOG(INFO) << "Couldn't register on selected network, trying home network";
+ Register(callback);
+ return;
+ }
+ callback.Run(error);
+}
+
+bool CellularCapabilityGSM::IsRegistered() const {
+ return (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
+ registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
+}
+
+void CellularCapabilityGSM::SetUnregistered(bool searching) {
+ // If we're already in some non-registered state, don't override that
+ if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
+ registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
+ registration_state_ =
+ (searching ? MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING :
+ MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE);
+ }
+}
+
+void CellularCapabilityGSM::RequirePIN(
+ const std::string &pin, bool require,
+ Error *error, const ResultCallback &callback) {
+ CHECK(error);
+ card_proxy_->EnablePIN(pin, require, error, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityGSM::EnterPIN(const string &pin,
+ Error *error,
+ const ResultCallback &callback) {
+ CHECK(error);
+ card_proxy_->SendPIN(pin, error, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityGSM::UnblockPIN(const string &unblock_code,
+ const string &pin,
+ Error *error,
+ const ResultCallback &callback) {
+ CHECK(error);
+ card_proxy_->SendPUK(unblock_code, pin, error, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityGSM::ChangePIN(
+ const string &old_pin, const string &new_pin,
+ Error *error, const ResultCallback &callback) {
+ CHECK(error);
+ card_proxy_->ChangePIN(old_pin, new_pin, error, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityGSM::Scan(Error *error,
+ const ResultStringmapsCallback &callback) {
+ ScanResultsCallback cb = Bind(&CellularCapabilityGSM::OnScanReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ network_proxy_->Scan(error, cb, kTimeoutScan);
+}
+
+void CellularCapabilityGSM::OnScanReply(
+ const ResultStringmapsCallback &callback,
+ const GSMScanResults &results,
+ const Error &error) {
+ Stringmaps found_networks;
+ for (const auto &result : results)
+ found_networks.push_back(ParseScanResult(result));
+ callback.Run(found_networks, error);
+}
+
+Stringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult &result) {
+ Stringmap parsed;
+ for (GSMScanResult::const_iterator it = result.begin();
+ it != result.end(); ++it) {
+ // TODO(petkov): Define these in system_api/service_constants.h. The
+ // numerical values are taken from 3GPP TS 27.007 Section 7.3.
+ static const char * const kStatusString[] = {
+ "unknown",
+ "available",
+ "current",
+ "forbidden",
+ };
+ static const char * const kTechnologyString[] = {
+ kNetworkTechnologyGsm,
+ "GSM Compact",
+ kNetworkTechnologyUmts,
+ kNetworkTechnologyEdge,
+ "HSDPA",
+ "HSUPA",
+ kNetworkTechnologyHspa,
+ };
+ SLOG(this, 2) << "Network property: " << it->first << " = "
+ << it->second;
+ if (it->first == kNetworkPropertyStatus) {
+ int status = 0;
+ if (base::StringToInt(it->second, &status) &&
+ status >= 0 &&
+ status < static_cast<int>(arraysize(kStatusString))) {
+ parsed[kStatusProperty] = kStatusString[status];
+ } else {
+ LOG(ERROR) << "Unexpected status value: " << it->second;
+ }
+ } else if (it->first == kNetworkPropertyID) {
+ parsed[kNetworkIdProperty] = it->second;
+ } else if (it->first == kNetworkPropertyLongName) {
+ parsed[kLongNameProperty] = it->second;
+ } else if (it->first == kNetworkPropertyShortName) {
+ parsed[kShortNameProperty] = it->second;
+ } else if (it->first == kNetworkPropertyAccessTechnology) {
+ int tech = 0;
+ if (base::StringToInt(it->second, &tech) &&
+ tech >= 0 &&
+ tech < static_cast<int>(arraysize(kTechnologyString))) {
+ parsed[kTechnologyProperty] = kTechnologyString[tech];
+ } else {
+ LOG(ERROR) << "Unexpected technology value: " << it->second;
+ }
+ } else {
+ LOG(WARNING) << "Unknown network property ignored: " << it->first;
+ }
+ }
+ // If the long name is not available but the network ID is, look up the long
+ // name in the mobile provider database.
+ if ((!ContainsKey(parsed, kLongNameProperty) ||
+ parsed[kLongNameProperty].empty()) &&
+ ContainsKey(parsed, kNetworkIdProperty)) {
+ mobile_operator_info_->Reset();
+ mobile_operator_info_->UpdateMCCMNC(parsed[kNetworkIdProperty]);
+ if (mobile_operator_info_->IsMobileNetworkOperatorKnown() &&
+ !mobile_operator_info_->operator_name().empty()) {
+ parsed[kLongNameProperty] = mobile_operator_info_->operator_name();
+ }
+ }
+ return parsed;
+}
+
+void CellularCapabilityGSM::SetAccessTechnology(uint32_t access_technology) {
+ access_technology_ = access_technology;
+ if (cellular()->service().get()) {
+ cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
+ }
+}
+
+string CellularCapabilityGSM::GetNetworkTechnologyString() const {
+ switch (access_technology_) {
+ case MM_MODEM_GSM_ACCESS_TECH_GSM:
+ case MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT:
+ return kNetworkTechnologyGsm;
+ case MM_MODEM_GSM_ACCESS_TECH_GPRS:
+ return kNetworkTechnologyGprs;
+ case MM_MODEM_GSM_ACCESS_TECH_EDGE:
+ return kNetworkTechnologyEdge;
+ case MM_MODEM_GSM_ACCESS_TECH_UMTS:
+ return kNetworkTechnologyUmts;
+ case MM_MODEM_GSM_ACCESS_TECH_HSDPA:
+ case MM_MODEM_GSM_ACCESS_TECH_HSUPA:
+ case MM_MODEM_GSM_ACCESS_TECH_HSPA:
+ return kNetworkTechnologyHspa;
+ case MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS:
+ return kNetworkTechnologyHspaPlus;
+ default:
+ break;
+ }
+ return "";
+}
+
+string CellularCapabilityGSM::GetRoamingStateString() const {
+ switch (registration_state_) {
+ case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
+ return kRoamingStateHome;
+ case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
+ return kRoamingStateRoaming;
+ default:
+ break;
+ }
+ return kRoamingStateUnknown;
+}
+
+void CellularCapabilityGSM::OnDBusPropertiesChanged(
+ const string &interface,
+ const DBusPropertiesMap &properties,
+ const vector<string> &invalidated_properties) {
+ CellularCapabilityClassic::OnDBusPropertiesChanged(interface,
+ properties,
+ invalidated_properties);
+ if (interface == MM_MODEM_GSM_NETWORK_INTERFACE) {
+ uint32_t access_technology = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ if (DBusProperties::GetUint32(properties,
+ kPropertyAccessTechnology,
+ &access_technology)) {
+ SetAccessTechnology(access_technology);
+ }
+ } else {
+ bool emit = false;
+ if (interface == MM_MODEM_GSM_CARD_INTERFACE) {
+ uint32_t locks = 0;
+ if (DBusProperties::GetUint32(
+ properties, kPropertyEnabledFacilityLocks, &locks)) {
+ sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
+ emit = true;
+ }
+ } else if (interface == MM_MODEM_INTERFACE) {
+ if (DBusProperties::GetString(properties, kPropertyUnlockRequired,
+ &sim_lock_status_.lock_type)) {
+ emit = true;
+ }
+ if (DBusProperties::GetUint32(properties, kPropertyUnlockRetries,
+ &sim_lock_status_.retries_left)) {
+ emit = true;
+ }
+ }
+ // TODO(pprabhu) Rename |emit| to |sim_present| after |sim_lock_status|
+ // moves to cellular.
+ if (emit) {
+ cellular()->set_sim_present(true);
+ cellular()->adaptor()->EmitKeyValueStoreChanged(
+ kSIMLockStatusProperty, SimLockStatusToProperty(nullptr));
+ }
+ }
+}
+
+void CellularCapabilityGSM::OnNetworkModeSignal(uint32_t /*mode*/) {
+ // TODO(petkov): Implement this.
+ NOTIMPLEMENTED();
+}
+
+void CellularCapabilityGSM::OnRegistrationInfoSignal(
+ uint32_t status, const string &operator_code, const string &operator_name) {
+ SLOG(this, 2) << __func__ << ": regstate=" << status
+ << ", opercode=" << operator_code
+ << ", opername=" << operator_name;
+ registration_state_ = status;
+ cellular()->serving_operator_info()->UpdateMCCMNC(operator_code);
+ cellular()->serving_operator_info()->UpdateOperatorName(operator_name);
+ cellular()->HandleNewRegistrationState();
+}
+
+void CellularCapabilityGSM::OnSignalQualitySignal(uint32_t quality) {
+ cellular()->HandleNewSignalQuality(quality);
+}
+
+void CellularCapabilityGSM::OnGetRegistrationInfoReply(
+ uint32_t status, const string &operator_code, const string &operator_name,
+ const Error &error) {
+ if (error.IsSuccess())
+ OnRegistrationInfoSignal(status, operator_code, operator_name);
+}
+
+void CellularCapabilityGSM::OnGetSignalQualityReply(uint32_t quality,
+ const Error &error) {
+ if (error.IsSuccess())
+ OnSignalQualitySignal(quality);
+}
+
+void CellularCapabilityGSM::OnGetIMEIReply(const ResultCallback &callback,
+ const string &imei,
+ const Error &error) {
+ if (error.IsSuccess()) {
+ SLOG(this, 2) << "IMEI: " << imei;
+ cellular()->set_imei(imei);
+ } else {
+ SLOG(this, 2) << "GetIMEI failed - " << error;
+ }
+ callback.Run(error);
+}
+
+void CellularCapabilityGSM::OnGetIMSIReply(const ResultCallback &callback,
+ const string &imsi,
+ const Error &error) {
+ if (error.IsSuccess()) {
+ SLOG(this, 2) << "IMSI: " << imsi;
+ cellular()->set_imsi(imsi);
+ cellular()->set_sim_present(true);
+ cellular()->home_provider_info()->UpdateIMSI(imsi);
+ // We do not currently obtain the IMSI OTA at all. Provide the IMSI from the
+ // SIM to the serving operator as well to aid in MVNO identification.
+ cellular()->serving_operator_info()->UpdateIMSI(imsi);
+ callback.Run(error);
+ } else if (!sim_lock_status_.lock_type.empty()) {
+ SLOG(this, 2) << "GetIMSI failed - SIM lock in place.";
+ cellular()->set_sim_present(true);
+ callback.Run(error);
+ } else {
+ cellular()->set_sim_present(false);
+ if (get_imsi_retries_++ < kGetIMSIRetryLimit) {
+ SLOG(this, 2) << "GetIMSI failed - " << error << ". Retrying";
+ base::Callback<void(void)> retry_get_imsi_cb =
+ Bind(&CellularCapabilityGSM::GetIMSI,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ cellular()->dispatcher()->PostDelayedTask(
+ retry_get_imsi_cb,
+ get_imsi_retry_delay_milliseconds_);
+ } else {
+ LOG(INFO) << "GetIMSI failed - " << error;
+ cellular()->home_provider_info()->Reset();
+ callback.Run(error);
+ }
+ }
+}
+
+void CellularCapabilityGSM::OnGetSPNReply(const ResultCallback &callback,
+ const string &spn,
+ const Error &error) {
+ if (error.IsSuccess()) {
+ SLOG(this, 2) << "SPN: " << spn;
+ spn_ = spn;
+ cellular()->home_provider_info()->UpdateOperatorName(spn);
+ } else {
+ SLOG(this, 2) << "GetSPN failed - " << error;
+ }
+ callback.Run(error);
+}
+
+void CellularCapabilityGSM::OnGetMSISDNReply(const ResultCallback &callback,
+ const string &msisdn,
+ const Error &error) {
+ if (error.IsSuccess()) {
+ SLOG(this, 2) << "MSISDN: " << msisdn;
+ cellular()->set_mdn(msisdn);
+ } else {
+ SLOG(this, 2) << "GetMSISDN failed - " << error;
+ }
+ callback.Run(error);
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_gsm.h b/cellular/cellular_capability_gsm.h
new file mode 100644
index 0000000..f98b979
--- /dev/null
+++ b/cellular/cellular_capability_gsm.h
@@ -0,0 +1,231 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_CAPABILITY_GSM_H_
+#define SHILL_CELLULAR_CELLULAR_CAPABILITY_GSM_H_
+
+#include <deque>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/memory/weak_ptr.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/accessor_interface.h"
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_capability.h"
+#include "shill/cellular/cellular_capability_classic.h"
+#include "shill/cellular/modem_gsm_card_proxy_interface.h"
+#include "shill/cellular/modem_gsm_network_proxy_interface.h"
+
+struct mobile_provider;
+
+namespace shill {
+
+class ModemInfo;
+
+class CellularCapabilityGSM : public CellularCapabilityClassic {
+ public:
+ CellularCapabilityGSM(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info);
+ ~CellularCapabilityGSM() override;
+
+ // Inherited from CellularCapability.
+ std::string GetTypeString() const override;
+ void OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties) override;
+ void StartModem(Error *error, const ResultCallback &callback) override;
+ bool AreProxiesInitialized() const override;
+ void Scan(Error *error, const ResultStringmapsCallback &callback) override;
+ void RegisterOnNetwork(const std::string &network_id,
+ Error *error,
+ const ResultCallback &callback) override;
+ bool IsRegistered() const override;
+ void SetUnregistered(bool searching) override;
+ void OnServiceCreated() override;
+ std::string GetNetworkTechnologyString() const override;
+ std::string GetRoamingStateString() const override;
+ bool AllowRoaming() override;
+ void GetSignalQuality() override;
+ void SetupConnectProperties(DBusPropertiesMap *properties) override;
+ void Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback) override;
+ void RequirePIN(const std::string &pin,
+ bool require,
+ Error *error,
+ const ResultCallback &callback) override;
+ void EnterPIN(const std::string &pin,
+ Error *error,
+ const ResultCallback &callback) override;
+ void UnblockPIN(const std::string &unblock_code,
+ const std::string &pin,
+ Error *error,
+ const ResultCallback &callback) override;
+ void ChangePIN(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error,
+ const ResultCallback &callback) override;
+
+ // Inherited from CellularCapabilityClassic.
+ void GetRegistrationState() override;
+ // The following six methods are only ever called as callbacks (from the main
+ // loop), which is why they don't take an Error * argument.
+ void GetProperties(const ResultCallback &callback) override;
+
+ virtual void GetIMEI(const ResultCallback &callback);
+ virtual void GetIMSI(const ResultCallback &callback);
+ virtual void GetSPN(const ResultCallback &callback);
+ virtual void GetMSISDN(const ResultCallback &callback);
+ virtual void Register(const ResultCallback &callback);
+
+ protected:
+ // Inherited from CellularCapabilityClassic.
+ void InitProxies() override;
+ void ReleaseProxies() override;
+
+ // Initializes properties, such as IMSI, which are required before the device
+ // is enabled.
+ virtual void InitProperties();
+
+ private:
+ friend class CellularTest;
+ friend class CellularCapabilityGSMTest;
+ friend class CellularCapabilityTest;
+ FRIEND_TEST(CellularCapabilityGSMTest, AllowRoaming);
+ FRIEND_TEST(CellularCapabilityGSMTest, CreateDeviceFromProperties);
+ FRIEND_TEST(CellularCapabilityGSMTest, GetIMEI);
+ FRIEND_TEST(CellularCapabilityGSMTest, GetIMSI);
+ FRIEND_TEST(CellularCapabilityGSMTest, GetIMSIFails);
+ FRIEND_TEST(CellularCapabilityGSMTest, GetMSISDN);
+ FRIEND_TEST(CellularCapabilityGSMTest, GetSPN);
+ FRIEND_TEST(CellularCapabilityGSMTest, RequirePIN);
+ FRIEND_TEST(CellularCapabilityGSMTest, EnterPIN);
+ FRIEND_TEST(CellularCapabilityGSMTest, UnblockPIN);
+ FRIEND_TEST(CellularCapabilityGSMTest, ChangePIN);
+ FRIEND_TEST(CellularCapabilityGSMTest, ParseScanResult);
+ FRIEND_TEST(CellularCapabilityGSMTest, ParseScanResultProviderLookup);
+ FRIEND_TEST(CellularCapabilityGSMTest, RegisterOnNetwork);
+ FRIEND_TEST(CellularCapabilityGSMTest, SetAccessTechnology);
+ FRIEND_TEST(CellularCapabilityGSMTest, GetRegistrationState);
+ FRIEND_TEST(CellularCapabilityGSMTest, OnDBusPropertiesChanged);
+ FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
+ FRIEND_TEST(CellularCapabilityTest, TryApns);
+ FRIEND_TEST(CellularTest, ScanAsynchronousFailure);
+ FRIEND_TEST(CellularTest, ScanImmediateFailure);
+ FRIEND_TEST(CellularTest, ScanSuccess);
+ FRIEND_TEST(CellularTest, StartGSMRegister);
+ FRIEND_TEST(ModemTest, CreateDeviceFromProperties);
+
+ // SimLockStatus represents the fields in the Cellular.SIMLockStatus
+ // DBUS property of the shill device.
+ struct SimLockStatus {
+ public:
+ SimLockStatus() : enabled(false), retries_left(0) {}
+
+ bool enabled;
+ std::string lock_type;
+ uint32_t retries_left;
+ };
+
+ static const char kNetworkPropertyAccessTechnology[];
+ static const char kNetworkPropertyID[];
+ static const char kNetworkPropertyLongName[];
+ static const char kNetworkPropertyShortName[];
+ static const char kNetworkPropertyStatus[];
+ static const char kPhoneNumber[];
+ static const char kPropertyAccessTechnology[];
+ static const char kPropertyEnabledFacilityLocks[];
+ static const char kPropertyUnlockRequired[];
+ static const char kPropertyUnlockRetries[];
+
+ // Calls to the proxy's GetIMSI() will be retried this many times.
+ static const int kGetIMSIRetryLimit;
+
+ // This much time will pass between retries of GetIMSI().
+ static const int64_t kGetIMSIRetryDelayMilliseconds;
+
+ void SetAccessTechnology(uint32_t access_technology);
+
+ Stringmap ParseScanResult(const GSMScanResult &result);
+
+ KeyValueStore SimLockStatusToProperty(Error *error);
+
+ void SetupApnTryList();
+ void FillConnectPropertyMap(DBusPropertiesMap *properties);
+
+ void HelpRegisterConstDerivedKeyValueStore(
+ const std::string &name,
+ KeyValueStore(CellularCapabilityGSM::*get)(Error *error));
+
+ bool IsUnderlyingDeviceRegistered() const;
+
+ // Signal callbacks
+ void OnNetworkModeSignal(uint32_t mode);
+ void OnRegistrationInfoSignal(uint32_t status,
+ const std::string &operator_code,
+ const std::string &operator_name);
+ void OnSignalQualitySignal(uint32_t quality);
+
+ // Method callbacks
+ void OnGetRegistrationInfoReply(uint32_t status,
+ const std::string &operator_code,
+ const std::string &operator_name,
+ const Error &error);
+ void OnGetSignalQualityReply(uint32_t quality, const Error &error);
+ void OnRegisterReply(const ResultCallback &callback,
+ const Error &error);
+ void OnGetIMEIReply(const ResultCallback &callback,
+ const std::string &imei,
+ const Error &error);
+ void OnGetIMSIReply(const ResultCallback &callback,
+ const std::string &imsi,
+ const Error &error);
+ void OnGetSPNReply(const ResultCallback &callback,
+ const std::string &spn,
+ const Error &error);
+ void OnGetMSISDNReply(const ResultCallback &callback,
+ const std::string &msisdn,
+ const Error &error);
+ void OnScanReply(const ResultStringmapsCallback &callback,
+ const GSMScanResults &results,
+ const Error &error);
+ void OnConnectReply(const ResultCallback &callback, const Error &error);
+
+ std::unique_ptr<ModemGSMCardProxyInterface> card_proxy_;
+ std::unique_ptr<ModemGSMNetworkProxyInterface> network_proxy_;
+ base::WeakPtrFactory<CellularCapabilityGSM> weak_ptr_factory_;
+ // Used to enrich information about the network operator in |ParseScanResult|.
+ // TODO(pprabhu) Instead instantiate a local |MobileOperatorInfo| instance
+ // once the context has been separated out. (crbug.com/363874)
+ std::unique_ptr<MobileOperatorInfo> mobile_operator_info_;
+
+ uint32_t registration_state_;
+ uint32_t access_technology_;
+ std::string spn_;
+ mobile_provider *home_provider_info_;
+ std::string desired_network_;
+
+ // The number of times GetIMSI() has been retried.
+ int get_imsi_retries_;
+
+ // Amount of time to wait between retries of GetIMSI. Defaults to
+ // kGetIMSIRetryDelayMilliseconds, but can be altered by a unit test.
+ int64_t get_imsi_retry_delay_milliseconds_;
+
+ // Properties.
+ std::deque<Stringmap> apn_try_list_;
+ SimLockStatus sim_lock_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(CellularCapabilityGSM);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_CAPABILITY_GSM_H_
diff --git a/cellular/cellular_capability_gsm_unittest.cc b/cellular/cellular_capability_gsm_unittest.cc
new file mode 100644
index 0000000..7c57e0e
--- /dev/null
+++ b/cellular/cellular_capability_gsm_unittest.cc
@@ -0,0 +1,805 @@
+// Copyright (c) 2012 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/cellular_capability_gsm.h"
+
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <mm/mm-modem.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/mock_mobile_operator_info.h"
+#include "shill/cellular/mock_modem_gsm_card_proxy.h"
+#include "shill/cellular/mock_modem_gsm_network_proxy.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/cellular/mock_modem_proxy.h"
+#include "shill/cellular/mock_modem_simple_proxy.h"
+#include "shill/error.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_adaptors.h"
+#include "shill/mock_log.h"
+#include "shill/mock_profile.h"
+#include "shill/proxy_factory.h"
+#include "shill/testing.h"
+
+using base::Bind;
+using base::StringPrintf;
+using base::Unretained;
+using std::string;
+using std::vector;
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+using testing::SaveArg;
+
+namespace shill {
+
+class CellularCapabilityGSMTest : public testing::Test {
+ public:
+ CellularCapabilityGSMTest()
+ : modem_info_(nullptr, &dispatcher_, nullptr, nullptr, nullptr),
+ create_card_proxy_from_factory_(false),
+ proxy_(new MockModemProxy()),
+ simple_proxy_(new MockModemSimpleProxy()),
+ card_proxy_(new MockModemGSMCardProxy()),
+ network_proxy_(new MockModemGSMNetworkProxy()),
+ proxy_factory_(this),
+ capability_(nullptr),
+ device_adaptor_(nullptr),
+ cellular_(new Cellular(&modem_info_,
+ "",
+ kAddress,
+ 0,
+ Cellular::kTypeGSM,
+ "",
+ "",
+ "",
+ &proxy_factory_)),
+ mock_home_provider_info_(nullptr),
+ mock_serving_operator_info_(nullptr) {
+ modem_info_.metrics()->RegisterDevice(cellular_->interface_index(),
+ Technology::kCellular);
+ }
+
+ virtual ~CellularCapabilityGSMTest() {
+ cellular_->service_ = nullptr;
+ capability_ = nullptr;
+ device_adaptor_ = nullptr;
+ }
+
+ virtual void SetUp() {
+ capability_ =
+ dynamic_cast<CellularCapabilityGSM *>(cellular_->capability_.get());
+ device_adaptor_ =
+ dynamic_cast<DeviceMockAdaptor *>(cellular_->adaptor());
+ }
+
+ void InvokeEnable(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeGetIMEI(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run(kIMEI, Error());
+ }
+ void InvokeGetIMSI(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run(kIMSI, Error());
+ }
+ void InvokeGetIMSIFails(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run("", Error(Error::kOperationFailed));
+ }
+ void InvokeGetMSISDN(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run(kMSISDN, Error());
+ }
+ void InvokeGetMSISDNFail(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run("", Error(Error::kOperationFailed));
+ }
+ void InvokeGetSPN(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run(kTestCarrier, Error());
+ }
+ void InvokeGetSPNFail(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run("", Error(Error::kOperationFailed));
+ }
+ void InvokeGetSignalQuality(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout) {
+ callback.Run(kStrength, Error());
+ }
+ void InvokeGetRegistrationInfo(Error *error,
+ const RegistrationInfoCallback &callback,
+ int timeout) {
+ callback.Run(MM_MODEM_GSM_NETWORK_REG_STATUS_HOME,
+ kTestNetwork, kTestCarrier, Error());
+ }
+ void InvokeRegister(const string &network_id,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeEnablePIN(const string &pin, bool enable,
+ Error *error, const ResultCallback &callback,
+ int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeSendPIN(const string &pin, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeSendPUK(const string &puk, const string &pin, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeChangePIN(const string &old_pin, const string &pin, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeGetModemStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout) {
+ DBusPropertiesMap props;
+ callback.Run(props, Error());
+ }
+ void InvokeGetModemInfo(Error *error, const ModemInfoCallback &callback,
+ int timeout) {
+ ModemHardwareInfo info;
+ callback.Run(info, Error());
+ }
+
+ void InvokeConnectFail(DBusPropertiesMap props, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error(Error::kOperationFailed));
+ }
+
+ MOCK_METHOD1(TestCallback, void(const Error &error));
+
+ protected:
+ static const char kAddress[];
+ static const char kTestMobileProviderDBPath[];
+ static const char kTestNetwork[];
+ static const char kTestCarrier[];
+ static const char kPIN[];
+ static const char kPUK[];
+ static const char kIMEI[];
+ static const char kIMSI[];
+ static const char kMSISDN[];
+ static const int kStrength;
+
+ class TestProxyFactory : public ProxyFactory {
+ public:
+ explicit TestProxyFactory(CellularCapabilityGSMTest *test) : test_(test) {}
+
+ virtual ModemProxyInterface *CreateModemProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ return test_->proxy_.release();
+ }
+
+ virtual ModemSimpleProxyInterface *CreateModemSimpleProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ return test_->simple_proxy_.release();
+ }
+
+ virtual ModemGSMCardProxyInterface *CreateModemGSMCardProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ // TODO(benchan): This code conditionally returns a nullptr to avoid
+ // CellularCapabilityGSM::InitProperties (and thus
+ // CellularCapabilityGSM::GetIMSI) from being called during the
+ // construction. Remove this workaround after refactoring the tests.
+ return test_->create_card_proxy_from_factory_ ?
+ test_->card_proxy_.release() : nullptr;
+ }
+
+ virtual ModemGSMNetworkProxyInterface *CreateModemGSMNetworkProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ return test_->network_proxy_.release();
+ }
+
+ private:
+ CellularCapabilityGSMTest *test_;
+ };
+
+ void SetProxy() {
+ capability_->proxy_.reset(proxy_.release());
+ }
+
+ void SetCardProxy() {
+ capability_->card_proxy_.reset(card_proxy_.release());
+ }
+
+ void SetNetworkProxy() {
+ capability_->network_proxy_.reset(network_proxy_.release());
+ }
+
+ void SetAccessTechnology(uint32_t technology) {
+ capability_->access_technology_ = technology;
+ }
+
+ void SetRegistrationState(uint32_t state) {
+ capability_->registration_state_ = state;
+ }
+
+ void CreateService() {
+ // The following constants are never directly accessed by the tests.
+ const char kStorageIdentifier[] = "default_test_storage_id";
+ const char kFriendlyServiceName[] = "default_test_service_name";
+ const char kOperatorCode[] = "10010";
+ const char kOperatorName[] = "default_test_operator_name";
+ const char kOperatorCountry[] = "us";
+
+ // Simulate all the side-effects of Cellular::CreateService
+ auto service = new CellularService(&modem_info_, cellular_);
+ service->SetStorageIdentifier(kStorageIdentifier);
+ service->SetFriendlyName(kFriendlyServiceName);
+
+ Stringmap serving_operator;
+ serving_operator[kOperatorCodeKey] = kOperatorCode;
+ serving_operator[kOperatorNameKey] = kOperatorName;
+ serving_operator[kOperatorCountryKey] = kOperatorCountry;
+
+ service->set_serving_operator(serving_operator);
+ cellular_->set_home_provider(serving_operator);
+ cellular_->service_ = service;
+ }
+
+ void SetMockMobileOperatorInfoObjects() {
+ CHECK(!mock_home_provider_info_);
+ CHECK(!mock_serving_operator_info_);
+ mock_home_provider_info_ =
+ new MockMobileOperatorInfo(&dispatcher_, "HomeProvider");
+ mock_serving_operator_info_ =
+ new MockMobileOperatorInfo(&dispatcher_, "ServingOperator");
+ cellular_->set_home_provider_info(mock_home_provider_info_);
+ cellular_->set_serving_operator_info(mock_serving_operator_info_);
+ }
+
+ void SetupCommonProxiesExpectations() {
+ EXPECT_CALL(*proxy_, set_state_changed_callback(_));
+ EXPECT_CALL(*network_proxy_, set_signal_quality_callback(_));
+ EXPECT_CALL(*network_proxy_, set_network_mode_callback(_));
+ EXPECT_CALL(*network_proxy_, set_registration_info_callback(_));
+ }
+
+ void SetupCommonStartModemExpectations() {
+ SetupCommonProxiesExpectations();
+
+ EXPECT_CALL(*proxy_, Enable(_, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeEnable));
+ EXPECT_CALL(*card_proxy_,
+ GetIMEI(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetIMEI));
+ EXPECT_CALL(*card_proxy_,
+ GetIMSI(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetIMSI));
+ EXPECT_CALL(*network_proxy_, AccessTechnology());
+ EXPECT_CALL(*card_proxy_, EnabledFacilityLocks());
+ EXPECT_CALL(*proxy_,
+ GetModemInfo(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetModemInfo));
+ EXPECT_CALL(*network_proxy_,
+ GetRegistrationInfo(_, _, CellularCapability::kTimeoutDefault));
+ EXPECT_CALL(*network_proxy_,
+ GetSignalQuality(_, _, CellularCapability::kTimeoutDefault));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ }
+
+ void InitProxies() {
+ AllowCreateCardProxyFromFactory();
+ capability_->InitProxies();
+ }
+
+ void AllowCreateCardProxyFromFactory() {
+ create_card_proxy_from_factory_ = true;
+ }
+
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ bool create_card_proxy_from_factory_;
+ std::unique_ptr<MockModemProxy> proxy_;
+ std::unique_ptr<MockModemSimpleProxy> simple_proxy_;
+ std::unique_ptr<MockModemGSMCardProxy> card_proxy_;
+ std::unique_ptr<MockModemGSMNetworkProxy> network_proxy_;
+ TestProxyFactory proxy_factory_;
+ CellularCapabilityGSM *capability_; // Owned by |cellular_|.
+ DeviceMockAdaptor *device_adaptor_; // Owned by |cellular_|.
+ CellularRefPtr cellular_;
+
+ // Set when required and passed to |cellular_|. Owned by |cellular_|.
+ MockMobileOperatorInfo *mock_home_provider_info_;
+ MockMobileOperatorInfo *mock_serving_operator_info_;
+};
+
+const char CellularCapabilityGSMTest::kAddress[] = "1122334455";
+const char CellularCapabilityGSMTest::kTestMobileProviderDBPath[] =
+ "provider_db_unittest.bfd";
+const char CellularCapabilityGSMTest::kTestCarrier[] = "The Cellular Carrier";
+const char CellularCapabilityGSMTest::kTestNetwork[] = "310555";
+const char CellularCapabilityGSMTest::kPIN[] = "9876";
+const char CellularCapabilityGSMTest::kPUK[] = "8765";
+const char CellularCapabilityGSMTest::kIMEI[] = "987654321098765";
+const char CellularCapabilityGSMTest::kIMSI[] = "310150123456789";
+const char CellularCapabilityGSMTest::kMSISDN[] = "12345678901";
+const int CellularCapabilityGSMTest::kStrength = 80;
+
+TEST_F(CellularCapabilityGSMTest, PropertyStore) {
+ EXPECT_TRUE(cellular_->store().Contains(kSIMLockStatusProperty));
+}
+
+TEST_F(CellularCapabilityGSMTest, GetIMEI) {
+ EXPECT_CALL(*card_proxy_, GetIMEI(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityGSMTest::InvokeGetIMEI));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetCardProxy();
+ ASSERT_TRUE(cellular_->imei().empty());
+ capability_->GetIMEI(Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+ EXPECT_EQ(kIMEI, cellular_->imei());
+}
+
+TEST_F(CellularCapabilityGSMTest, GetIMSI) {
+ SetMockMobileOperatorInfoObjects();
+ EXPECT_CALL(*card_proxy_, GetIMSI(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityGSMTest::InvokeGetIMSI));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetCardProxy();
+ ResultCallback callback = Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this));
+ EXPECT_TRUE(cellular_->imsi().empty());
+ EXPECT_FALSE(cellular_->sim_present());
+ EXPECT_CALL(*mock_home_provider_info_, UpdateIMSI(kIMSI));
+ capability_->GetIMSI(callback);
+ EXPECT_EQ(kIMSI, cellular_->imsi());
+ EXPECT_TRUE(cellular_->sim_present());
+}
+
+// In this test, the call to the proxy's GetIMSI() will always indicate failure,
+// which will cause the retry logic to call the proxy again a number of times.
+// Eventually, the retries expire.
+TEST_F(CellularCapabilityGSMTest, GetIMSIFails) {
+ ScopedMockLog log;
+ EXPECT_CALL(log, Log(logging::LOG_INFO,
+ ::testing::EndsWith("cellular_capability_gsm.cc"),
+ ::testing::StartsWith("GetIMSI failed - ")));
+ EXPECT_CALL(*card_proxy_, GetIMSI(_, _, CellularCapability::kTimeoutDefault))
+ .Times(CellularCapabilityGSM::kGetIMSIRetryLimit + 2)
+ .WillRepeatedly(Invoke(this,
+ &CellularCapabilityGSMTest::InvokeGetIMSIFails));
+ EXPECT_CALL(*this, TestCallback(IsFailure())).Times(2);
+ SetCardProxy();
+ ResultCallback callback = Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this));
+ EXPECT_TRUE(cellular_->imsi().empty());
+ EXPECT_FALSE(cellular_->sim_present());
+
+ capability_->sim_lock_status_.lock_type = "sim-pin";
+ capability_->GetIMSI(callback);
+ EXPECT_TRUE(cellular_->imsi().empty());
+ EXPECT_TRUE(cellular_->sim_present());
+
+ capability_->sim_lock_status_.lock_type.clear();
+ cellular_->set_sim_present(false);
+ capability_->get_imsi_retries_ = 0;
+ EXPECT_EQ(CellularCapabilityGSM::kGetIMSIRetryDelayMilliseconds,
+ capability_->get_imsi_retry_delay_milliseconds_);
+
+ // Set the delay to zero to speed up the test.
+ capability_->get_imsi_retry_delay_milliseconds_ = 0;
+ capability_->GetIMSI(callback);
+ for (int i = 0; i < CellularCapabilityGSM::kGetIMSIRetryLimit; ++i) {
+ dispatcher_.DispatchPendingEvents();
+ }
+ EXPECT_EQ(CellularCapabilityGSM::kGetIMSIRetryLimit + 1,
+ capability_->get_imsi_retries_);
+ EXPECT_TRUE(cellular_->imsi().empty());
+ EXPECT_FALSE(cellular_->sim_present());
+}
+
+TEST_F(CellularCapabilityGSMTest, GetMSISDN) {
+ EXPECT_CALL(*card_proxy_, GetMSISDN(_, _,
+ CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityGSMTest::InvokeGetMSISDN));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetCardProxy();
+ ASSERT_TRUE(cellular_->mdn().empty());
+ capability_->GetMSISDN(Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+ EXPECT_EQ(kMSISDN, cellular_->mdn());
+}
+
+TEST_F(CellularCapabilityGSMTest, GetSPN) {
+ EXPECT_CALL(*card_proxy_, GetSPN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityGSMTest::InvokeGetSPN));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetCardProxy();
+ ASSERT_TRUE(capability_->spn_.empty());
+ capability_->GetSPN(Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+ EXPECT_EQ(kTestCarrier, capability_->spn_);
+}
+
+TEST_F(CellularCapabilityGSMTest, GetSignalQuality) {
+ EXPECT_CALL(*network_proxy_,
+ GetSignalQuality(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityGSMTest::InvokeGetSignalQuality));
+ SetNetworkProxy();
+ CreateService();
+ EXPECT_EQ(0, cellular_->service()->strength());
+ capability_->GetSignalQuality();
+ EXPECT_EQ(kStrength, cellular_->service()->strength());
+}
+
+TEST_F(CellularCapabilityGSMTest, RegisterOnNetwork) {
+ EXPECT_CALL(*network_proxy_, Register(kTestNetwork, _, _,
+ CellularCapability::kTimeoutRegister))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeRegister));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetNetworkProxy();
+ Error error;
+ capability_->RegisterOnNetwork(kTestNetwork, &error,
+ Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+ EXPECT_EQ(kTestNetwork, cellular_->selected_network());
+}
+
+TEST_F(CellularCapabilityGSMTest, IsRegistered) {
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE);
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_HOME);
+ EXPECT_TRUE(capability_->IsRegistered());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING);
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED);
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN);
+ EXPECT_FALSE(capability_->IsRegistered());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
+ EXPECT_TRUE(capability_->IsRegistered());
+}
+
+TEST_F(CellularCapabilityGSMTest, GetRegistrationState) {
+ ASSERT_FALSE(capability_->IsRegistered());
+ EXPECT_CALL(*network_proxy_,
+ GetRegistrationInfo(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this,
+ &CellularCapabilityGSMTest::InvokeGetRegistrationInfo));
+ SetNetworkProxy();
+ capability_->GetRegistrationState();
+ EXPECT_TRUE(capability_->IsRegistered());
+ EXPECT_EQ(MM_MODEM_GSM_NETWORK_REG_STATUS_HOME,
+ capability_->registration_state_);
+}
+
+TEST_F(CellularCapabilityGSMTest, RequirePIN) {
+ EXPECT_CALL(*card_proxy_, EnablePIN(kPIN, true, _, _,
+ CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeEnablePIN));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetCardProxy();
+ Error error;
+ capability_->RequirePIN(kPIN, true, &error,
+ Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+ EXPECT_TRUE(error.IsSuccess());
+}
+
+TEST_F(CellularCapabilityGSMTest, EnterPIN) {
+ EXPECT_CALL(*card_proxy_, SendPIN(kPIN, _, _,
+ CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeSendPIN));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetCardProxy();
+ Error error;
+ capability_->EnterPIN(kPIN, &error,
+ Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+ EXPECT_TRUE(error.IsSuccess());
+}
+
+TEST_F(CellularCapabilityGSMTest, UnblockPIN) {
+ EXPECT_CALL(*card_proxy_, SendPUK(kPUK, kPIN, _, _,
+ CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeSendPUK));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetCardProxy();
+ Error error;
+ capability_->UnblockPIN(kPUK, kPIN, &error,
+ Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+ EXPECT_TRUE(error.IsSuccess());
+}
+
+TEST_F(CellularCapabilityGSMTest, ChangePIN) {
+ static const char kOldPIN[] = "1111";
+ EXPECT_CALL(*card_proxy_, ChangePIN(kOldPIN, kPIN, _, _,
+ CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeChangePIN));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetCardProxy();
+ Error error;
+ capability_->ChangePIN(kOldPIN, kPIN, &error,
+ Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+ EXPECT_TRUE(error.IsSuccess());
+}
+
+
+TEST_F(CellularCapabilityGSMTest, ParseScanResult) {
+ static const char kID[] = "123";
+ static const char kLongName[] = "long name";
+ static const char kShortName[] = "short name";
+ GSMScanResult result;
+ result[CellularCapabilityGSM::kNetworkPropertyStatus] = "1";
+ result[CellularCapabilityGSM::kNetworkPropertyID] = kID;
+ result[CellularCapabilityGSM::kNetworkPropertyLongName] = kLongName;
+ result[CellularCapabilityGSM::kNetworkPropertyShortName] = kShortName;
+ result[CellularCapabilityGSM::kNetworkPropertyAccessTechnology] = "3";
+ result["unknown property"] = "random value";
+ Stringmap parsed = capability_->ParseScanResult(result);
+ EXPECT_EQ(5, parsed.size());
+ EXPECT_EQ("available", parsed[kStatusProperty]);
+ EXPECT_EQ(kID, parsed[kNetworkIdProperty]);
+ EXPECT_EQ(kLongName, parsed[kLongNameProperty]);
+ EXPECT_EQ(kShortName, parsed[kShortNameProperty]);
+ EXPECT_EQ(kNetworkTechnologyEdge, parsed[kTechnologyProperty]);
+}
+
+TEST_F(CellularCapabilityGSMTest, ParseScanResultProviderLookup) {
+ static const char kID[] = "10001";
+ const string kLongName = "TestNetworkLongName";
+ // Replace the |MobileOperatorInfo| used by |ParseScanResult| by a mock.
+ auto *mock_mobile_operator_info = new MockMobileOperatorInfo(
+ &dispatcher_,
+ "MockParseScanResult");
+ capability_->mobile_operator_info_.reset(mock_mobile_operator_info);
+
+ mock_mobile_operator_info->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_mobile_operator_info, UpdateMCCMNC(kID));
+ EXPECT_CALL(*mock_mobile_operator_info, IsMobileNetworkOperatorKnown()).
+ WillOnce(Return(true));
+ EXPECT_CALL(*mock_mobile_operator_info, operator_name()).
+ WillRepeatedly(ReturnRef(kLongName));
+ GSMScanResult result;
+ result[CellularCapabilityGSM::kNetworkPropertyID] = kID;
+ Stringmap parsed = capability_->ParseScanResult(result);
+ EXPECT_EQ(2, parsed.size());
+ EXPECT_EQ(kID, parsed[kNetworkIdProperty]);
+ EXPECT_EQ(kLongName, parsed[kLongNameProperty]);
+}
+
+TEST_F(CellularCapabilityGSMTest, SetAccessTechnology) {
+ capability_->SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_GSM);
+ EXPECT_EQ(MM_MODEM_GSM_ACCESS_TECH_GSM, capability_->access_technology_);
+ CreateService();
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_HOME);
+ capability_->SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_GPRS);
+ EXPECT_EQ(MM_MODEM_GSM_ACCESS_TECH_GPRS, capability_->access_technology_);
+ EXPECT_EQ(kNetworkTechnologyGprs, cellular_->service()->network_technology());
+}
+
+TEST_F(CellularCapabilityGSMTest, AllowRoaming) {
+ EXPECT_FALSE(cellular_->allow_roaming_);
+ EXPECT_FALSE(cellular_->provider_requires_roaming());
+ EXPECT_FALSE(capability_->AllowRoaming());
+ cellular_->set_provider_requires_roaming(true);
+ EXPECT_TRUE(capability_->AllowRoaming());
+ cellular_->set_provider_requires_roaming(false);
+ cellular_->allow_roaming_ = true;
+ EXPECT_TRUE(capability_->AllowRoaming());
+}
+
+TEST_F(CellularCapabilityGSMTest, GetNetworkTechnologyString) {
+ EXPECT_EQ("", capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_GSM);
+ EXPECT_EQ(kNetworkTechnologyGsm, capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT);
+ EXPECT_EQ(kNetworkTechnologyGsm, capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_GPRS);
+ EXPECT_EQ(kNetworkTechnologyGprs, capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_EDGE);
+ EXPECT_EQ(kNetworkTechnologyEdge, capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_UMTS);
+ EXPECT_EQ(kNetworkTechnologyUmts, capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_HSDPA);
+ EXPECT_EQ(kNetworkTechnologyHspa, capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_HSUPA);
+ EXPECT_EQ(kNetworkTechnologyHspa, capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_HSPA);
+ EXPECT_EQ(kNetworkTechnologyHspa, capability_->GetNetworkTechnologyString());
+ SetAccessTechnology(MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS);
+ EXPECT_EQ(kNetworkTechnologyHspaPlus,
+ capability_->GetNetworkTechnologyString());
+}
+
+TEST_F(CellularCapabilityGSMTest, GetRoamingStateString) {
+ EXPECT_EQ(kRoamingStateUnknown, capability_->GetRoamingStateString());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_HOME);
+ EXPECT_EQ(kRoamingStateHome, capability_->GetRoamingStateString());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
+ EXPECT_EQ(kRoamingStateRoaming, capability_->GetRoamingStateString());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING);
+ EXPECT_EQ(kRoamingStateUnknown, capability_->GetRoamingStateString());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED);
+ EXPECT_EQ(kRoamingStateUnknown, capability_->GetRoamingStateString());
+ SetRegistrationState(MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE);
+ EXPECT_EQ(kRoamingStateUnknown, capability_->GetRoamingStateString());
+}
+
+MATCHER_P(KeyValueStoreEq, value, "") {
+ bool match = value.bool_properties() == arg.bool_properties() &&
+ value.int_properties() == arg.int_properties() &&
+ value.string_properties() == arg.string_properties() &&
+ value.uint_properties() == arg.uint_properties();
+ if (!match) {
+ *result_listener << "\nExpected KeyValueStore:\n"
+ << "\tbool_properties: "
+ << testing::PrintToString(value.bool_properties())
+ << "\n\tint_properties: "
+ << testing::PrintToString(value.int_properties())
+ << "\n\tstring_properties: "
+ << testing::PrintToString(value.string_properties())
+ << "\n\tint_properties: "
+ << testing::PrintToString(value.uint_properties())
+ << "\nGot KeyValueStore:\n"
+ << "\tbool_properties: "
+ << testing::PrintToString(arg.bool_properties())
+ << "\n\tint_properties: "
+ << testing::PrintToString(arg.int_properties())
+ << "\n\tstring_properties: "
+ << testing::PrintToString(arg.string_properties())
+ << "\n\tuint_properties: "
+ << testing::PrintToString(arg.uint_properties());
+ }
+ return match;
+}
+
+TEST_F(CellularCapabilityGSMTest, OnDBusPropertiesChanged) {
+ EXPECT_EQ(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN, capability_->access_technology_);
+ EXPECT_FALSE(capability_->sim_lock_status_.enabled);
+ EXPECT_EQ("", capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(0, capability_->sim_lock_status_.retries_left);
+ DBusPropertiesMap props;
+ static const char kLockType[] = "sim-pin";
+ const int kRetries = 3;
+ props[CellularCapabilityGSM::kPropertyAccessTechnology].writer().
+ append_uint32(MM_MODEM_GSM_ACCESS_TECH_EDGE);
+ props[CellularCapabilityGSM::kPropertyEnabledFacilityLocks].writer().
+ append_uint32(MM_MODEM_GSM_FACILITY_SIM);
+ props[CellularCapabilityGSM::kPropertyUnlockRequired].writer().append_string(
+ kLockType);
+ props[CellularCapabilityGSM::kPropertyUnlockRetries].writer().append_uint32(
+ kRetries);
+ // Call with the 'wrong' interface and nothing should change.
+ capability_->OnDBusPropertiesChanged(MM_MODEM_GSM_INTERFACE, props,
+ vector<string>());
+ EXPECT_EQ(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN, capability_->access_technology_);
+ EXPECT_FALSE(capability_->sim_lock_status_.enabled);
+ EXPECT_EQ("", capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(0, capability_->sim_lock_status_.retries_left);
+
+ // Call with the MM_MODEM_GSM_NETWORK_INTERFACE interface and expect a change
+ // to the enabled state of the SIM lock.
+ KeyValueStore lock_status;
+ lock_status.SetBool(kSIMLockEnabledProperty, true);
+ lock_status.SetString(kSIMLockTypeProperty, "");
+ lock_status.SetUint(kSIMLockRetriesLeftProperty, 0);
+
+ EXPECT_CALL(*device_adaptor_, EmitKeyValueStoreChanged(
+ kSIMLockStatusProperty,
+ KeyValueStoreEq(lock_status)));
+
+ capability_->OnDBusPropertiesChanged(MM_MODEM_GSM_NETWORK_INTERFACE, props,
+ vector<string>());
+ EXPECT_EQ(MM_MODEM_GSM_ACCESS_TECH_EDGE, capability_->access_technology_);
+ capability_->OnDBusPropertiesChanged(MM_MODEM_GSM_CARD_INTERFACE, props,
+ vector<string>());
+ EXPECT_TRUE(capability_->sim_lock_status_.enabled);
+ EXPECT_TRUE(capability_->sim_lock_status_.lock_type.empty());
+ EXPECT_EQ(0, capability_->sim_lock_status_.retries_left);
+
+ // Some properties are sent on the MM_MODEM_INTERFACE.
+ capability_->sim_lock_status_.enabled = false;
+ capability_->sim_lock_status_.lock_type = "";
+ capability_->sim_lock_status_.retries_left = 0;
+ KeyValueStore lock_status2;
+ lock_status2.SetBool(kSIMLockEnabledProperty, false);
+ lock_status2.SetString(kSIMLockTypeProperty, kLockType);
+ lock_status2.SetUint(kSIMLockRetriesLeftProperty, kRetries);
+ EXPECT_CALL(*device_adaptor_,
+ EmitKeyValueStoreChanged(kSIMLockStatusProperty,
+ KeyValueStoreEq(lock_status2)));
+ capability_->OnDBusPropertiesChanged(MM_MODEM_INTERFACE, props,
+ vector<string>());
+ EXPECT_FALSE(capability_->sim_lock_status_.enabled);
+ EXPECT_EQ(kLockType, capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(kRetries, capability_->sim_lock_status_.retries_left);
+}
+
+TEST_F(CellularCapabilityGSMTest, StartModemSuccess) {
+ SetupCommonStartModemExpectations();
+ EXPECT_CALL(*card_proxy_,
+ GetSPN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetSPN));
+ EXPECT_CALL(*card_proxy_,
+ GetMSISDN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetMSISDN));
+ AllowCreateCardProxyFromFactory();
+
+ Error error;
+ capability_->StartModem(
+ &error, Bind(&CellularCapabilityGSMTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularCapabilityGSMTest, StartModemGetSPNFail) {
+ SetupCommonStartModemExpectations();
+ EXPECT_CALL(*card_proxy_,
+ GetSPN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetSPNFail));
+ EXPECT_CALL(*card_proxy_,
+ GetMSISDN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetMSISDN));
+ AllowCreateCardProxyFromFactory();
+
+ Error error;
+ capability_->StartModem(
+ &error, Bind(&CellularCapabilityGSMTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularCapabilityGSMTest, StartModemGetMSISDNFail) {
+ SetupCommonStartModemExpectations();
+ EXPECT_CALL(*card_proxy_,
+ GetSPN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetSPN));
+ EXPECT_CALL(*card_proxy_,
+ GetMSISDN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeGetMSISDNFail));
+ AllowCreateCardProxyFromFactory();
+
+ Error error;
+ capability_->StartModem(
+ &error, Bind(&CellularCapabilityGSMTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularCapabilityGSMTest, ConnectFailureNoService) {
+ // Make sure we don't crash if the connect failed and there is no
+ // CellularService object. This can happen if the modem is enabled and
+ // then quickly disabled.
+ SetupCommonProxiesExpectations();
+ EXPECT_CALL(*simple_proxy_,
+ Connect(_, _, _, CellularCapabilityGSM::kTimeoutConnect))
+ .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeConnectFail));
+ EXPECT_CALL(*this, TestCallback(IsFailure()));
+ InitProxies();
+ EXPECT_FALSE(capability_->cellular()->service());
+ Error error;
+ DBusPropertiesMap props;
+ capability_->Connect(props, &error,
+ Bind(&CellularCapabilityGSMTest::TestCallback,
+ Unretained(this)));
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_universal.cc b/cellular/cellular_capability_universal.cc
new file mode 100644
index 0000000..f3fb0fd
--- /dev/null
+++ b/cellular/cellular_capability_universal.cc
@@ -0,0 +1,1699 @@
+// Copyright (c) 2013 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/cellular_capability_universal.h"
+
+#include <base/bind.h>
+#include <base/stl_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <ModemManager/ModemManager.h>
+
+#include <string>
+#include <vector>
+
+#include "shill/adaptor_interfaces.h"
+#include "shill/cellular/cellular_bearer.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/mobile_operator_info.h"
+#include "shill/dbus_properties_proxy_interface.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+#include "shill/pending_activation_store.h"
+#include "shill/property_accessor.h"
+#include "shill/proxy_factory.h"
+
+#ifdef MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN
+#error "Do not include mm-modem.h"
+#endif
+
+using base::Bind;
+using base::Closure;
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularCapabilityUniversal *c) {
+ return c->cellular()->GetRpcIdentifier();
+}
+}
+
+// static
+const char CellularCapabilityUniversal::kConnectPin[] = "pin";
+const char CellularCapabilityUniversal::kConnectOperatorId[] = "operator-id";
+const char CellularCapabilityUniversal::kConnectApn[] = "apn";
+const char CellularCapabilityUniversal::kConnectIPType[] = "ip-type";
+const char CellularCapabilityUniversal::kConnectUser[] = "user";
+const char CellularCapabilityUniversal::kConnectPassword[] = "password";
+const char CellularCapabilityUniversal::kConnectNumber[] = "number";
+const char CellularCapabilityUniversal::kConnectAllowRoaming[] =
+ "allow-roaming";
+const char CellularCapabilityUniversal::kConnectRMProtocol[] = "rm-protocol";
+const int64_t CellularCapabilityUniversal::kEnterPinTimeoutMilliseconds = 20000;
+const int64_t
+CellularCapabilityUniversal::kRegistrationDroppedUpdateTimeoutMilliseconds =
+ 15000;
+const char CellularCapabilityUniversal::kRootPath[] = "/";
+const char CellularCapabilityUniversal::kStatusProperty[] = "status";
+const char CellularCapabilityUniversal::kOperatorLongProperty[] =
+ "operator-long";
+const char CellularCapabilityUniversal::kOperatorShortProperty[] =
+ "operator-short";
+const char CellularCapabilityUniversal::kOperatorCodeProperty[] =
+ "operator-code";
+const char CellularCapabilityUniversal::kOperatorAccessTechnologyProperty[] =
+ "access-technology";
+const char CellularCapabilityUniversal::kAltairLTEMMPlugin[] = "Altair LTE";
+const char CellularCapabilityUniversal::kNovatelLTEMMPlugin[] = "Novatel LTE";
+const int CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds =
+ 20000;
+
+namespace {
+
+const char kPhoneNumber[] = "*99#";
+
+// This identifier is specified in the serviceproviders.prototxt file.
+const char kVzwIdentifier[] = "c83d6597-dc91-4d48-a3a7-d86b80123751";
+const size_t kVzwMdnLength = 10;
+
+string AccessTechnologyToString(uint32_t access_technologies) {
+ if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
+ return kNetworkTechnologyLte;
+ if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDOB))
+ return kNetworkTechnologyEvdo;
+ if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_1XRTT)
+ return kNetworkTechnology1Xrtt;
+ if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)
+ return kNetworkTechnologyHspaPlus;
+ if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
+ MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
+ MM_MODEM_ACCESS_TECHNOLOGY_HSDPA))
+ return kNetworkTechnologyHspa;
+ if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
+ return kNetworkTechnologyUmts;
+ if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_EDGE)
+ return kNetworkTechnologyEdge;
+ if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_GPRS)
+ return kNetworkTechnologyGprs;
+ if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM))
+ return kNetworkTechnologyGsm;
+ return "";
+}
+
+string AccessTechnologyToTechnologyFamily(uint32_t access_technologies) {
+ if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_LTE |
+ MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS |
+ MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
+ MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
+ MM_MODEM_ACCESS_TECHNOLOGY_HSDPA |
+ MM_MODEM_ACCESS_TECHNOLOGY_UMTS |
+ MM_MODEM_ACCESS_TECHNOLOGY_EDGE |
+ MM_MODEM_ACCESS_TECHNOLOGY_GPRS |
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM))
+ return kTechnologyFamilyGsm;
+ if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDOB |
+ MM_MODEM_ACCESS_TECHNOLOGY_1XRTT))
+ return kTechnologyFamilyCdma;
+ return "";
+}
+
+} // namespace
+
+CellularCapabilityUniversal::CellularCapabilityUniversal(
+ Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info)
+ : CellularCapability(cellular, proxy_factory, modem_info),
+ mobile_operator_info_(new MobileOperatorInfo(cellular->dispatcher(),
+ "ParseScanResult")),
+ weak_ptr_factory_(this),
+ registration_state_(MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN),
+ current_capabilities_(MM_MODEM_CAPABILITY_NONE),
+ access_technologies_(MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
+ resetting_(false),
+ subscription_state_(kSubscriptionStateUnknown),
+ reset_done_(false),
+ registration_dropped_update_timeout_milliseconds_(
+ kRegistrationDroppedUpdateTimeoutMilliseconds) {
+ SLOG(this, 2) << "Cellular capability constructed: Universal";
+ mobile_operator_info_->Init();
+ HelpRegisterConstDerivedKeyValueStore(
+ kSIMLockStatusProperty,
+ &CellularCapabilityUniversal::SimLockStatusToProperty);
+}
+
+CellularCapabilityUniversal::~CellularCapabilityUniversal() {}
+
+KeyValueStore CellularCapabilityUniversal::SimLockStatusToProperty(
+ Error */*error*/) {
+ KeyValueStore status;
+ string lock_type;
+ switch (sim_lock_status_.lock_type) {
+ case MM_MODEM_LOCK_SIM_PIN:
+ lock_type = "sim-pin";
+ break;
+ case MM_MODEM_LOCK_SIM_PUK:
+ lock_type = "sim-puk";
+ break;
+ default:
+ lock_type = "";
+ break;
+ }
+ status.SetBool(kSIMLockEnabledProperty, sim_lock_status_.enabled);
+ status.SetString(kSIMLockTypeProperty, lock_type);
+ status.SetUint(kSIMLockRetriesLeftProperty, sim_lock_status_.retries_left);
+ return status;
+}
+
+void CellularCapabilityUniversal::HelpRegisterConstDerivedKeyValueStore(
+ const string &name,
+ KeyValueStore(CellularCapabilityUniversal::*get)(Error *error)) {
+ cellular()->mutable_store()->RegisterDerivedKeyValueStore(
+ name,
+ KeyValueStoreAccessor(
+ new CustomAccessor<CellularCapabilityUniversal, KeyValueStore>(
+ this, get, nullptr)));
+}
+
+void CellularCapabilityUniversal::InitProxies() {
+ modem_3gpp_proxy_.reset(
+ proxy_factory()->CreateMM1ModemModem3gppProxy(cellular()->dbus_path(),
+ cellular()->dbus_owner()));
+ modem_proxy_.reset(
+ proxy_factory()->CreateMM1ModemProxy(cellular()->dbus_path(),
+ cellular()->dbus_owner()));
+ modem_simple_proxy_.reset(
+ proxy_factory()->CreateMM1ModemSimpleProxy(cellular()->dbus_path(),
+ cellular()->dbus_owner()));
+
+ modem_proxy_->set_state_changed_callback(
+ Bind(&CellularCapabilityUniversal::OnModemStateChangedSignal,
+ weak_ptr_factory_.GetWeakPtr()));
+ // Do not create a SIM proxy until the device is enabled because we
+ // do not yet know the object path of the sim object.
+ // TODO(jglasgow): register callbacks
+}
+
+void CellularCapabilityUniversal::StartModem(Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 3) << __func__;
+ InitProxies();
+ deferred_enable_modem_callback_.Reset();
+ EnableModem(true, error, callback);
+}
+
+void CellularCapabilityUniversal::EnableModem(bool deferrable,
+ Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 3) << __func__ << "(deferrable=" << deferrable << ")";
+ CHECK(!callback.is_null());
+ Error local_error(Error::kOperationInitiated);
+ modem_info()->metrics()->NotifyDeviceEnableStarted(
+ cellular()->interface_index());
+ modem_proxy_->Enable(
+ true,
+ &local_error,
+ Bind(&CellularCapabilityUniversal::EnableModemCompleted,
+ weak_ptr_factory_.GetWeakPtr(), deferrable, callback),
+ kTimeoutEnable);
+ if (local_error.IsFailure()) {
+ SLOG(this, 2) << __func__ << "Call to modem_proxy_->Enable() failed";
+ }
+ if (error) {
+ error->CopyFrom(local_error);
+ }
+}
+
+void CellularCapabilityUniversal::EnableModemCompleted(
+ bool deferrable, const ResultCallback &callback, const Error &error) {
+ SLOG(this, 3) << __func__ << "(deferrable=" << deferrable
+ << ", error=" << error << ")";
+
+ // If the enable operation failed with Error::kWrongState, the modem is not
+ // in the expected state (i.e. disabled). If |deferrable| indicates that the
+ // enable operation can be deferred, we defer the operation until the modem
+ // goes into the expected state (see OnModemStateChangedSignal).
+ //
+ // Note that when the SIM is locked, the enable operation also fails with
+ // Error::kWrongState. The enable operation is deferred until the modem goes
+ // into the disabled state after the SIM is unlocked. We may choose not to
+ // defer the enable operation when the SIM is locked, but the UI needs to
+ // trigger the enable operation after the SIM is unlocked, which is currently
+ // not the case.
+ if (error.IsFailure()) {
+ if (!deferrable || error.type() != Error::kWrongState) {
+ callback.Run(error);
+ return;
+ }
+
+ if (deferred_enable_modem_callback_.is_null()) {
+ SLOG(this, 2) << "Defer enable operation.";
+ // The Enable operation to be deferred should not be further deferrable.
+ deferred_enable_modem_callback_ =
+ Bind(&CellularCapabilityUniversal::EnableModem,
+ weak_ptr_factory_.GetWeakPtr(),
+ false, // non-deferrable
+ nullptr,
+ callback);
+ }
+ return;
+ }
+
+ // After modem is enabled, it should be possible to get properties
+ // TODO(jglasgow): handle errors from GetProperties
+ GetProperties();
+ // We expect the modem to start scanning after it has been enabled.
+ // Change this if this behavior is no longer the case in the future.
+ modem_info()->metrics()->NotifyDeviceEnableFinished(
+ cellular()->interface_index());
+ modem_info()->metrics()->NotifyDeviceScanStarted(
+ cellular()->interface_index());
+ callback.Run(error);
+}
+
+void CellularCapabilityUniversal::StopModem(Error *error,
+ const ResultCallback &callback) {
+ CHECK(!callback.is_null());
+ CHECK(error);
+ // If there is an outstanding registration change, simply ignore it since
+ // the service will be destroyed anyway.
+ if (!registration_dropped_update_callback_.IsCancelled()) {
+ registration_dropped_update_callback_.Cancel();
+ SLOG(this, 2) << __func__ << " Cancelled delayed deregister.";
+ }
+
+ // Some modems will implicitly disconnect the bearer when transitioning to
+ // low power state. For such modems, it's faster to let the modem disconnect
+ // the bearer. To do that, we just remove the bearer from the list so
+ // ModemManager doesn't try to disconnect it during disable.
+ Closure task;
+ if (cellular()->mm_plugin() == kAltairLTEMMPlugin) {
+ task = Bind(&CellularCapabilityUniversal::Stop_DeleteActiveBearer,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ } else {
+ task = Bind(&CellularCapabilityUniversal::Stop_Disable,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ }
+ cellular()->dispatcher()->PostTask(task);
+ deferred_enable_modem_callback_.Reset();
+}
+
+void CellularCapabilityUniversal::Stop_DeleteActiveBearer(
+ const ResultCallback &callback) {
+ SLOG(this, 3) << __func__;
+
+ if (!active_bearer_) {
+ Stop_Disable(callback);
+ return;
+ }
+
+ Error error;
+ modem_proxy_->DeleteBearer(
+ active_bearer_->dbus_path(), &error,
+ Bind(&CellularCapabilityUniversal::Stop_DeleteActiveBearerCompleted,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ kTimeoutDefault);
+ if (error.IsFailure())
+ callback.Run(error);
+}
+
+void CellularCapabilityUniversal::Stop_DeleteActiveBearerCompleted(
+ const ResultCallback &callback, const Error &error) {
+ SLOG(this, 3) << __func__;
+ // Disregard the error from the bearer deletion since the disable will clean
+ // up any remaining bearers.
+ Stop_Disable(callback);
+}
+
+void CellularCapabilityUniversal::Stop_Disable(const ResultCallback &callback) {
+ SLOG(this, 3) << __func__;
+ Error error;
+ modem_info()->metrics()->NotifyDeviceDisableStarted(
+ cellular()->interface_index());
+ modem_proxy_->Enable(
+ false, &error,
+ Bind(&CellularCapabilityUniversal::Stop_DisableCompleted,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ kTimeoutEnable);
+ if (error.IsFailure())
+ callback.Run(error);
+}
+
+void CellularCapabilityUniversal::Stop_DisableCompleted(
+ const ResultCallback &callback, const Error &error) {
+ SLOG(this, 3) << __func__;
+
+ if (error.IsSuccess()) {
+ // The modem has been successfully disabled, but we still need to power it
+ // down.
+ Stop_PowerDown(callback);
+ } else {
+ // An error occurred; terminate the disable sequence.
+ callback.Run(error);
+ }
+}
+
+void CellularCapabilityUniversal::Stop_PowerDown(
+ const ResultCallback &callback) {
+ SLOG(this, 3) << __func__;
+ Error error;
+ modem_proxy_->SetPowerState(
+ MM_MODEM_POWER_STATE_LOW,
+ &error,
+ Bind(&CellularCapabilityUniversal::Stop_PowerDownCompleted,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ kSetPowerStateTimeoutMilliseconds);
+
+ if (error.IsFailure())
+ // This really shouldn't happen, but if it does, report success,
+ // because a stop initiated power down is only called if the
+ // modem was successfully disabled, but the failure of this
+ // operation should still be propagated up as a successful disable.
+ Stop_PowerDownCompleted(callback, error);
+}
+
+// Note: if we were in the middle of powering down the modem when the
+// system suspended, we might not get this event from
+// ModemManager. And we might not even get a timeout from dbus-c++,
+// because StartModem re-initializes proxies.
+void CellularCapabilityUniversal::Stop_PowerDownCompleted(
+ const ResultCallback &callback,
+ const Error &error) {
+ SLOG(this, 3) << __func__;
+
+ if (error.IsFailure())
+ SLOG(this, 2) << "Ignoring error returned by SetPowerState: " << error;
+
+ // Since the disable succeeded, if power down fails, we currently fail
+ // silently, i.e. we need to report the disable operation as having
+ // succeeded.
+ modem_info()->metrics()->NotifyDeviceDisableFinished(
+ cellular()->interface_index());
+ ReleaseProxies();
+ callback.Run(Error());
+}
+
+void CellularCapabilityUniversal::Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 3) << __func__;
+ DBusPathCallback cb = Bind(&CellularCapabilityUniversal::OnConnectReply,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback);
+ modem_simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
+}
+
+void CellularCapabilityUniversal::Disconnect(Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 3) << __func__;
+ if (modem_simple_proxy_.get()) {
+ SLOG(this, 2) << "Disconnect all bearers.";
+ // If "/" is passed as the bearer path, ModemManager will disconnect all
+ // bearers.
+ modem_simple_proxy_->Disconnect(DBus::Path(kRootPath),
+ error,
+ callback,
+ kTimeoutDisconnect);
+ }
+}
+
+void CellularCapabilityUniversal::CompleteActivation(Error *error) {
+ SLOG(this, 3) << __func__;
+
+ // Persist the ICCID as "Pending Activation".
+ // We're assuming that when this function gets called,
+ // |cellular()->sim_identifier()| will be non-empty. We still check here that
+ // is non-empty, though something is wrong if it is empty.
+ const string &sim_identifier = cellular()->sim_identifier();
+ if (sim_identifier.empty()) {
+ SLOG(this, 2) << "SIM identifier not available. Nothing to do.";
+ return;
+ }
+
+ modem_info()->pending_activation_store()->SetActivationState(
+ PendingActivationStore::kIdentifierICCID,
+ sim_identifier,
+ PendingActivationStore::kStatePending);
+ UpdatePendingActivationState();
+
+ SLOG(this, 2) << "Resetting modem for activation.";
+ ResetAfterActivation();
+}
+
+void CellularCapabilityUniversal::ResetAfterActivation() {
+ SLOG(this, 3) << __func__;
+
+ // Here the initial call to Reset might fail in rare cases. Simply ignore.
+ Error error;
+ ResultCallback callback = Bind(
+ &CellularCapabilityUniversal::OnResetAfterActivationReply,
+ weak_ptr_factory_.GetWeakPtr());
+ Reset(&error, callback);
+ if (error.IsFailure())
+ SLOG(this, 2) << "Failed to reset after activation.";
+}
+
+void CellularCapabilityUniversal::OnResetAfterActivationReply(
+ const Error &error) {
+ SLOG(this, 3) << __func__;
+ if (error.IsFailure()) {
+ SLOG(this, 2) << "Failed to reset after activation. Try again later.";
+ // TODO(armansito): Maybe post a delayed reset task?
+ return;
+ }
+ reset_done_ = true;
+ UpdatePendingActivationState();
+}
+
+void CellularCapabilityUniversal::UpdatePendingActivationState() {
+ SLOG(this, 3) << __func__;
+
+ const string &sim_identifier = cellular()->sim_identifier();
+ bool registered =
+ registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
+
+ // We know a service is activated if |subscription_state_| is
+ // kSubscriptionStateProvisioned / kSubscriptionStateOutOfData
+ // In the case that |subscription_state_| is kSubscriptionStateUnknown, we
+ // fallback on checking for a valid MDN.
+ bool activated =
+ ((subscription_state_ == kSubscriptionStateProvisioned) ||
+ (subscription_state_ == kSubscriptionStateOutOfData)) ||
+ ((subscription_state_ == kSubscriptionStateUnknown) && IsMdnValid());
+
+ if (activated && !sim_identifier.empty())
+ modem_info()->pending_activation_store()->RemoveEntry(
+ PendingActivationStore::kIdentifierICCID,
+ sim_identifier);
+
+ CellularServiceRefPtr service = cellular()->service();
+
+ if (!service.get())
+ return;
+
+ if (service->activation_state() == kActivationStateActivated)
+ // Either no service or already activated. Nothing to do.
+ return;
+
+ // If the ICCID is not available, the following logic can be delayed until it
+ // becomes available.
+ if (sim_identifier.empty())
+ return;
+
+ PendingActivationStore::State state =
+ modem_info()->pending_activation_store()->GetActivationState(
+ PendingActivationStore::kIdentifierICCID,
+ sim_identifier);
+ switch (state) {
+ case PendingActivationStore::kStatePending:
+ // Always mark the service as activating here, as the ICCID could have
+ // been unavailable earlier.
+ service->SetActivationState(kActivationStateActivating);
+ if (reset_done_) {
+ SLOG(this, 2) << "Post-payment activation reset complete.";
+ modem_info()->pending_activation_store()->SetActivationState(
+ PendingActivationStore::kIdentifierICCID,
+ sim_identifier,
+ PendingActivationStore::kStateActivated);
+ }
+ break;
+ case PendingActivationStore::kStateActivated:
+ if (registered) {
+ // Trigger auto connect here.
+ SLOG(this, 2) << "Modem has been reset at least once, try to "
+ << "autoconnect to force MDN to update.";
+ service->AutoConnect();
+ }
+ break;
+ case PendingActivationStore::kStateUnknown:
+ // No entry exists for this ICCID. Nothing to do.
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+string CellularCapabilityUniversal::GetMdnForOLP(
+ const MobileOperatorInfo *operator_info) const {
+ // TODO(benchan): This is ugly. Remove carrier specific code once we move
+ // mobile activation logic to carrier-specifc extensions (crbug.com/260073).
+ const string &mdn = cellular()->mdn();
+ if (!operator_info->IsMobileNetworkOperatorKnown()) {
+ // Can't make any carrier specific modifications.
+ return mdn;
+ }
+
+ if (operator_info->uuid() == kVzwIdentifier) {
+ // subscription_state_ is the definitive indicator of whether we need
+ // activation. The OLP expects an all zero MDN in that case.
+ if (subscription_state_ == kSubscriptionStateUnprovisioned || mdn.empty()) {
+ return string(kVzwMdnLength, '0');
+ }
+ if (mdn.length() > kVzwMdnLength) {
+ return mdn.substr(mdn.length() - kVzwMdnLength);
+ }
+ }
+ return mdn;
+}
+
+void CellularCapabilityUniversal::ReleaseProxies() {
+ SLOG(this, 3) << __func__;
+ modem_3gpp_proxy_.reset();
+ modem_proxy_.reset();
+ modem_simple_proxy_.reset();
+ sim_proxy_.reset();
+}
+
+bool CellularCapabilityUniversal::AreProxiesInitialized() const {
+ return (modem_3gpp_proxy_.get() && modem_proxy_.get() &&
+ modem_simple_proxy_.get() && sim_proxy_.get());
+}
+
+void CellularCapabilityUniversal::UpdateServiceActivationState() {
+ if (!cellular()->service().get())
+ return;
+
+ const string &sim_identifier = cellular()->sim_identifier();
+ string activation_state;
+ PendingActivationStore::State state =
+ modem_info()->pending_activation_store()->GetActivationState(
+ PendingActivationStore::kIdentifierICCID,
+ sim_identifier);
+ if ((subscription_state_ == kSubscriptionStateUnknown ||
+ subscription_state_ == kSubscriptionStateUnprovisioned) &&
+ !sim_identifier.empty() &&
+ state == PendingActivationStore::kStatePending) {
+ activation_state = kActivationStateActivating;
+ } else if (IsServiceActivationRequired()) {
+ activation_state = kActivationStateNotActivated;
+ } else {
+ activation_state = kActivationStateActivated;
+
+ // Mark an activated service for auto-connect by default. Since data from
+ // the user profile will be loaded after the call to OnServiceCreated, this
+ // property will be corrected based on the user data at that time.
+ // NOTE: This function can be called outside the service initialization
+ // path so make sure we don't overwrite the auto-connect setting.
+ if (cellular()->service()->activation_state() != activation_state)
+ cellular()->service()->SetAutoConnect(true);
+ }
+ cellular()->service()->SetActivationState(activation_state);
+}
+
+void CellularCapabilityUniversal::OnServiceCreated() {
+ cellular()->service()->SetActivationType(CellularService::kActivationTypeOTA);
+ UpdateServiceActivationState();
+
+ // WORKAROUND:
+ // E362 modems on Verizon network does not properly redirect when a SIM
+ // runs out of credits, we need to enforce out-of-credits detection.
+ //
+ // The out-of-credits detection is also needed on ALT3100 modems until the PCO
+ // support is ready (crosbug.com/p/20461).
+ cellular()->service()->InitOutOfCreditsDetection(
+ GetOutOfCreditsDetectionType());
+
+ // Make sure that the network technology is set when the service gets
+ // created, just in case.
+ cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
+}
+
+// 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 CellularCapabilityUniversal::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(),
+ cellular()->apn_list().begin(),
+ cellular()->apn_list().end());
+}
+
+void CellularCapabilityUniversal::SetupConnectProperties(
+ DBusPropertiesMap *properties) {
+ SetupApnTryList();
+ FillConnectPropertyMap(properties);
+}
+
+void CellularCapabilityUniversal::FillConnectPropertyMap(
+ DBusPropertiesMap *properties) {
+
+ // TODO(jglasgow): Is this really needed anymore?
+ (*properties)[kConnectNumber].writer().append_string(
+ kPhoneNumber);
+
+ (*properties)[kConnectAllowRoaming].writer().append_bool(
+ AllowRoaming());
+
+ 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();
+ SLOG(this, 2) << __func__ << ": Using APN " << apn_info[kApnProperty];
+ (*properties)[kConnectApn].writer().append_string(
+ apn_info[kApnProperty].c_str());
+ if (ContainsKey(apn_info, kApnUsernameProperty))
+ (*properties)[kConnectUser].writer().append_string(
+ apn_info[kApnUsernameProperty].c_str());
+ if (ContainsKey(apn_info, kApnPasswordProperty))
+ (*properties)[kConnectPassword].writer().append_string(
+ apn_info[kApnPasswordProperty].c_str());
+ }
+}
+
+void CellularCapabilityUniversal::OnConnectReply(const ResultCallback &callback,
+ const DBus::Path &path,
+ const Error &error) {
+ SLOG(this, 3) << __func__ << "(" << error << ")";
+
+ CellularServiceRefPtr service = cellular()->service();
+ if (!service) {
+ // The service could have been deleted before our Connect() request
+ // completes if the modem was enabled and then quickly disabled.
+ apn_try_list_.clear();
+ } else if (error.IsFailure()) {
+ 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 (RetriableConnectError(error) && !apn_try_list_.empty()) {
+ apn_try_list_.pop_front();
+ SLOG(this, 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()) {
+ service->SetLastGoodApn(apn_try_list_.front());
+ apn_try_list_.clear();
+ }
+ SLOG(this, 2) << "Connected bearer " << path;
+ }
+
+ if (!callback.is_null())
+ callback.Run(error);
+
+ UpdatePendingActivationState();
+}
+
+bool CellularCapabilityUniversal::AllowRoaming() {
+ return cellular()->provider_requires_roaming() || allow_roaming_property();
+}
+
+void CellularCapabilityUniversal::GetProperties() {
+ SLOG(this, 3) << __func__;
+
+ std::unique_ptr<DBusPropertiesProxyInterface> properties_proxy(
+ proxy_factory()->CreateDBusPropertiesProxy(cellular()->dbus_path(),
+ cellular()->dbus_owner()));
+ DBusPropertiesMap properties(
+ properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM));
+ OnModemPropertiesChanged(properties, vector<string>());
+
+ properties = properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP);
+ OnModem3GPPPropertiesChanged(properties, vector<string>());
+}
+
+void CellularCapabilityUniversal::UpdateServiceOLP() {
+ SLOG(this, 3) << __func__;
+
+ // OLP is based off of the Home Provider.
+ if (!cellular()->home_provider_info()->IsMobileNetworkOperatorKnown()) {
+ return;
+ }
+
+ const vector<MobileOperatorInfo::OnlinePortal> &olp_list =
+ cellular()->home_provider_info()->olp_list();
+ if (olp_list.empty()) {
+ return;
+ }
+
+ if (olp_list.size() > 1) {
+ SLOG(this, 1) << "Found multiple online portals. Choosing the first.";
+ }
+ string post_data = olp_list[0].post_data;
+ ReplaceSubstringsAfterOffset(&post_data, 0, "${iccid}",
+ cellular()->sim_identifier());
+ ReplaceSubstringsAfterOffset(&post_data, 0, "${imei}", cellular()->imei());
+ ReplaceSubstringsAfterOffset(&post_data, 0, "${imsi}", cellular()->imsi());
+ ReplaceSubstringsAfterOffset(&post_data, 0, "${mdn}",
+ GetMdnForOLP(cellular()->home_provider_info()));
+ ReplaceSubstringsAfterOffset(&post_data, 0, "${min}", cellular()->min());
+ cellular()->service()->SetOLP(olp_list[0].url, olp_list[0].method, post_data);
+}
+
+void CellularCapabilityUniversal::UpdateActiveBearer() {
+ SLOG(this, 3) << __func__;
+
+ // Look for the first active bearer and use its path as the connected
+ // one. Right now, we don't allow more than one active bearer.
+ active_bearer_.reset();
+ for (const auto &path : bearer_paths_) {
+ std::unique_ptr<CellularBearer> bearer(
+ new CellularBearer(proxy_factory(), path, cellular()->dbus_service()));
+ // The bearer object may have vanished before ModemManager updates the
+ // 'Bearers' property.
+ if (!bearer->Init())
+ continue;
+
+ if (!bearer->connected())
+ continue;
+
+ SLOG(this, 2) << "Found active bearer \"" << path << "\".";
+ CHECK(!active_bearer_) << "Found more than one active bearer.";
+ active_bearer_ = std::move(bearer);
+ }
+
+ if (!active_bearer_)
+ SLOG(this, 2) << "No active bearer found.";
+}
+
+bool CellularCapabilityUniversal::IsServiceActivationRequired() const {
+ const string &sim_identifier = cellular()->sim_identifier();
+ // subscription_state_ is the definitive answer. If that does not work,
+ // fallback on MDN based logic.
+ if (subscription_state_ == kSubscriptionStateProvisioned ||
+ subscription_state_ == kSubscriptionStateOutOfData)
+ return false;
+
+ // We are in the process of activating, ignore all other clues from the
+ // network and use our own knowledge about the activation state.
+ if (!sim_identifier.empty() &&
+ modem_info()->pending_activation_store()->GetActivationState(
+ PendingActivationStore::kIdentifierICCID,
+ sim_identifier) != PendingActivationStore::kStateUnknown)
+ return false;
+
+ // Network notification that the service needs to be activated.
+ if (subscription_state_ == kSubscriptionStateUnprovisioned)
+ return true;
+
+ // If there is no online payment portal information, it's safer to assume
+ // the service does not require activation.
+ if (!cellular()->home_provider_info()->IsMobileNetworkOperatorKnown() ||
+ cellular()->home_provider_info()->olp_list().empty()) {
+ return false;
+ }
+
+ // If the MDN is invalid (i.e. empty or contains only zeros), the service
+ // requires activation.
+ return !IsMdnValid();
+}
+
+bool CellularCapabilityUniversal::IsMdnValid() const {
+ const string &mdn = cellular()->mdn();
+ // Note that |mdn| is normalized to contain only digits in OnMdnChanged().
+ for (size_t i = 0; i < mdn.size(); ++i) {
+ if (mdn[i] != '0')
+ return true;
+ }
+ return false;
+}
+
+// always called from an async context
+void CellularCapabilityUniversal::Register(const ResultCallback &callback) {
+ SLOG(this, 3) << __func__ << " \"" << cellular()->selected_network()
+ << "\"";
+ CHECK(!callback.is_null());
+ Error error;
+ ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ modem_3gpp_proxy_->Register(cellular()->selected_network(), &error, cb,
+ kTimeoutRegister);
+ if (error.IsFailure())
+ callback.Run(error);
+}
+
+void CellularCapabilityUniversal::RegisterOnNetwork(
+ const string &network_id,
+ Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 3) << __func__ << "(" << network_id << ")";
+ CHECK(error);
+ desired_network_ = network_id;
+ ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ modem_3gpp_proxy_->Register(network_id, error, cb, kTimeoutRegister);
+}
+
+void CellularCapabilityUniversal::OnRegisterReply(
+ const ResultCallback &callback,
+ const Error &error) {
+ SLOG(this, 3) << __func__ << "(" << error << ")";
+
+ if (error.IsSuccess()) {
+ cellular()->set_selected_network(desired_network_);
+ desired_network_.clear();
+ callback.Run(error);
+ return;
+ }
+ // If registration on the desired network failed,
+ // try to register on the home network.
+ if (!desired_network_.empty()) {
+ desired_network_.clear();
+ cellular()->set_selected_network("");
+ LOG(INFO) << "Couldn't register on selected network, trying home network";
+ Register(callback);
+ return;
+ }
+ callback.Run(error);
+}
+
+bool CellularCapabilityUniversal::IsRegistered() const {
+ return IsRegisteredState(registration_state_);
+}
+
+bool CellularCapabilityUniversal::IsRegisteredState(
+ MMModem3gppRegistrationState state) {
+ return (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
+ state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING);
+}
+
+void CellularCapabilityUniversal::SetUnregistered(bool searching) {
+ // If we're already in some non-registered state, don't override that
+ if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
+ registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
+ registration_state_ =
+ (searching ? MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING :
+ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
+ }
+}
+
+void CellularCapabilityUniversal::RequirePIN(
+ const string &pin, bool require,
+ Error *error, const ResultCallback &callback) {
+ CHECK(error);
+ sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityUniversal::EnterPIN(const string &pin,
+ Error *error,
+ const ResultCallback &callback) {
+ CHECK(error);
+ SLOG(this, 3) << __func__;
+ sim_proxy_->SendPin(pin, error, callback, kEnterPinTimeoutMilliseconds);
+}
+
+void CellularCapabilityUniversal::UnblockPIN(const string &unblock_code,
+ const string &pin,
+ Error *error,
+ const ResultCallback &callback) {
+ CHECK(error);
+ sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityUniversal::ChangePIN(
+ const string &old_pin, const string &new_pin,
+ Error *error, const ResultCallback &callback) {
+ CHECK(error);
+ sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
+}
+
+void CellularCapabilityUniversal::Reset(Error *error,
+ const ResultCallback &callback) {
+ SLOG(this, 3) << __func__;
+ CHECK(error);
+ if (resetting_) {
+ Error::PopulateAndLog(error, Error::kInProgress, "Already resetting");
+ return;
+ }
+ ResultCallback cb = Bind(&CellularCapabilityUniversal::OnResetReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ modem_proxy_->Reset(error, cb, kTimeoutReset);
+ if (!error->IsFailure()) {
+ resetting_ = true;
+ }
+}
+
+void CellularCapabilityUniversal::OnResetReply(const ResultCallback &callback,
+ const Error &error) {
+ SLOG(this, 3) << __func__;
+ resetting_ = false;
+ if (!callback.is_null())
+ callback.Run(error);
+}
+
+void CellularCapabilityUniversal::Scan(
+ Error *error,
+ const ResultStringmapsCallback &callback) {
+ DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+ modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
+}
+
+void CellularCapabilityUniversal::OnScanReply(
+ const ResultStringmapsCallback &callback,
+ const ScanResults &results,
+ const Error &error) {
+ Stringmaps found_networks;
+ for (const auto &result : results)
+ found_networks.push_back(ParseScanResult(result));
+ callback.Run(found_networks, error);
+}
+
+Stringmap CellularCapabilityUniversal::ParseScanResult(
+ const ScanResult &result) {
+
+ /* ScanResults contain the following keys:
+
+ "status"
+ A MMModem3gppNetworkAvailability value representing network
+ availability status, given as an unsigned integer (signature "u").
+ This key will always be present.
+
+ "operator-long"
+ Long-format name of operator, given as a string value (signature
+ "s"). If the name is unknown, this field should not be present.
+
+ "operator-short"
+ Short-format name of operator, given as a string value
+ (signature "s"). If the name is unknown, this field should not
+ be present.
+
+ "operator-code"
+ Mobile code of the operator, given as a string value (signature
+ "s"). Returned in the format "MCCMNC", where MCC is the
+ three-digit ITU E.212 Mobile Country Code and MNC is the two- or
+ three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
+
+ "access-technology"
+ A MMModemAccessTechnology value representing the generic access
+ technology used by this mobile network, given as an unsigned
+ integer (signature "u").
+ */
+ Stringmap parsed;
+
+ uint32_t status;
+ if (DBusProperties::GetUint32(result, kStatusProperty, &status)) {
+ // numerical values are taken from 3GPP TS 27.007 Section 7.3.
+ static const char * const kStatusString[] = {
+ "unknown", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
+ "available", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
+ "current", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
+ "forbidden", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
+ };
+ parsed[kStatusProperty] = kStatusString[status];
+ }
+
+ uint32_t tech; // MMModemAccessTechnology
+ if (DBusProperties::GetUint32(result, kOperatorAccessTechnologyProperty,
+ &tech)) {
+ parsed[kTechnologyProperty] = AccessTechnologyToString(tech);
+ }
+
+ string operator_long, operator_short, operator_code;
+ if (DBusProperties::GetString(result, kOperatorLongProperty, &operator_long))
+ parsed[kLongNameProperty] = operator_long;
+ if (DBusProperties::GetString(result, kOperatorShortProperty,
+ &operator_short))
+ parsed[kShortNameProperty] = operator_short;
+ if (DBusProperties::GetString(result, kOperatorCodeProperty, &operator_code))
+ parsed[kNetworkIdProperty] = operator_code;
+
+ // If the long name is not available but the network ID is, look up the long
+ // name in the mobile provider database.
+ if ((!ContainsKey(parsed, kLongNameProperty) ||
+ parsed[kLongNameProperty].empty()) &&
+ ContainsKey(parsed, kNetworkIdProperty)) {
+ mobile_operator_info_->Reset();
+ mobile_operator_info_->UpdateMCCMNC(parsed[kNetworkIdProperty]);
+ if (mobile_operator_info_->IsMobileNetworkOperatorKnown() &&
+ !mobile_operator_info_->operator_name().empty()) {
+ parsed[kLongNameProperty] = mobile_operator_info_->operator_name();
+ }
+ }
+ return parsed;
+}
+
+CellularBearer *CellularCapabilityUniversal::GetActiveBearer() const {
+ return active_bearer_.get();
+}
+
+string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
+ // If we know that the modem is an E362 modem supported by the Novatel LTE
+ // plugin, return LTE here to make sure that Chrome sees LTE as the network
+ // technology even if the actual technology is unknown.
+ //
+ // This hack will cause the UI to display LTE even if the modem doesn't
+ // support it at a given time. This might be problematic if we ever want to
+ // support switching between access technologies (e.g. falling back to 3G
+ // when LTE is not available).
+ if (cellular()->mm_plugin() == kNovatelLTEMMPlugin)
+ return kNetworkTechnologyLte;
+
+ // Order is important. Return the highest speed technology
+ // TODO(jglasgow): change shill interfaces to a capability model
+ return AccessTechnologyToString(access_technologies_);
+}
+
+string CellularCapabilityUniversal::GetRoamingStateString() const {
+ switch (registration_state_) {
+ case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
+ return kRoamingStateHome;
+ case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
+ return kRoamingStateRoaming;
+ default:
+ break;
+ }
+ return kRoamingStateUnknown;
+}
+
+// TODO(armansito): Remove this method once cromo is deprecated.
+void CellularCapabilityUniversal::GetSignalQuality() {
+ // ModemManager always returns the cached value, so there is no need to
+ // trigger an update here. The true value is updated through a property
+ // change signal.
+}
+
+string CellularCapabilityUniversal::GetTypeString() const {
+ return AccessTechnologyToTechnologyFamily(access_technologies_);
+}
+
+void CellularCapabilityUniversal::OnModemPropertiesChanged(
+ const DBusPropertiesMap &properties,
+ const vector<string> &/* invalidated_properties */) {
+
+ // Update the bearers property before the modem state property as
+ // OnModemStateChanged may call UpdateActiveBearer, which reads the bearers
+ // property.
+ RpcIdentifiers bearers;
+ if (DBusProperties::GetRpcIdentifiers(properties, MM_MODEM_PROPERTY_BEARERS,
+ &bearers)) {
+ OnBearersChanged(bearers);
+ }
+
+ // This solves a bootstrapping problem: If the modem is not yet
+ // enabled, there are no proxy objects associated with the capability
+ // object, so modem signals like StateChanged aren't seen. By monitoring
+ // changes to the State property via the ModemManager, we're able to
+ // get the initialization process started, which will result in the
+ // creation of the proxy objects.
+ //
+ // The first time we see the change to State (when the modem state
+ // is Unknown), we simply update the state, and rely on the Manager to
+ // enable the device when it is registered with the Manager. On subsequent
+ // changes to State, we need to explicitly enable the device ourselves.
+ int32_t istate;
+ if (DBusProperties::GetInt32(properties, MM_MODEM_PROPERTY_STATE, &istate)) {
+ Cellular::ModemState state = static_cast<Cellular::ModemState>(istate);
+ OnModemStateChanged(state);
+ }
+ DBus::Path object_path_value;
+ if (DBusProperties::GetObjectPath(properties,
+ MM_MODEM_PROPERTY_SIM, &object_path_value))
+ OnSimPathChanged(object_path_value);
+
+ DBusPropertiesMap::const_iterator it =
+ properties.find(MM_MODEM_PROPERTY_SUPPORTEDCAPABILITIES);
+ if (it != properties.end()) {
+ const vector<uint32_t> &supported_capabilities = it->second;
+ OnSupportedCapabilitesChanged(supported_capabilities);
+ }
+
+ uint32_t uint_value;
+ if (DBusProperties::GetUint32(properties,
+ MM_MODEM_PROPERTY_CURRENTCAPABILITIES,
+ &uint_value))
+ OnModemCurrentCapabilitiesChanged(uint_value);
+ // not needed: MM_MODEM_PROPERTY_MAXBEARERS
+ // not needed: MM_MODEM_PROPERTY_MAXACTIVEBEARERS
+ string string_value;
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_PROPERTY_MANUFACTURER,
+ &string_value))
+ cellular()->set_manufacturer(string_value);
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_PROPERTY_MODEL,
+ &string_value))
+ cellular()->set_model_id(string_value);
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_PROPERTY_PLUGIN,
+ &string_value))
+ cellular()->set_mm_plugin(string_value);
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_PROPERTY_REVISION,
+ &string_value))
+ OnModemRevisionChanged(string_value);
+ // not needed: MM_MODEM_PROPERTY_DEVICEIDENTIFIER
+ // not needed: MM_MODEM_PROPERTY_DEVICE
+ // not needed: MM_MODEM_PROPERTY_DRIVER
+ // not needed: MM_MODEM_PROPERTY_PLUGIN
+ // not needed: MM_MODEM_PROPERTY_EQUIPMENTIDENTIFIER
+
+ // Unlock required and SimLock
+ uint32_t unlock_required; // This is really of type MMModemLock
+ bool lock_status_changed = false;
+ if (DBusProperties::GetUint32(properties,
+ MM_MODEM_PROPERTY_UNLOCKREQUIRED,
+ &unlock_required)) {
+ OnLockTypeChanged(static_cast<MMModemLock>(unlock_required));
+ lock_status_changed = true;
+ }
+
+ // Unlock retries
+ it = properties.find(MM_MODEM_PROPERTY_UNLOCKRETRIES);
+ if (it != properties.end()) {
+ LockRetryData lock_retries = it->second.operator LockRetryData();
+ OnLockRetriesChanged(lock_retries);
+ lock_status_changed = true;
+ }
+
+ if (lock_status_changed)
+ OnSimLockStatusChanged();
+
+ if (DBusProperties::GetUint32(properties,
+ MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES,
+ &uint_value))
+ OnAccessTechnologiesChanged(uint_value);
+
+ it = properties.find(MM_MODEM_PROPERTY_SIGNALQUALITY);
+ if (it != properties.end()) {
+ DBus::Struct<unsigned int, bool> quality = it->second;
+ OnSignalQualityChanged(quality._1);
+ }
+ vector<string> numbers;
+ if (DBusProperties::GetStrings(properties, MM_MODEM_PROPERTY_OWNNUMBERS,
+ &numbers)) {
+ string mdn;
+ if (numbers.size() > 0)
+ mdn = numbers[0];
+ OnMdnChanged(mdn);
+ }
+
+ it = properties.find(MM_MODEM_PROPERTY_SUPPORTEDMODES);
+ if (it != properties.end()) {
+ const vector<DBus::Struct<uint32_t, uint32_t>> &mm_supported_modes =
+ it->second;
+ vector<ModemModes> supported_modes;
+ for (const auto &modes : mm_supported_modes) {
+ supported_modes.push_back(
+ ModemModes(modes._1, static_cast<MMModemMode>(modes._2)));
+ }
+ OnSupportedModesChanged(supported_modes);
+ }
+
+ it = properties.find(MM_MODEM_PROPERTY_CURRENTMODES);
+ if (it != properties.end()) {
+ const DBus::Struct<uint32_t, uint32_t> ¤t_modes = it->second;
+ OnCurrentModesChanged(ModemModes(
+ current_modes._1, static_cast<MMModemMode>(current_modes._2)));
+ }
+
+ // au: MM_MODEM_PROPERTY_SUPPORTEDBANDS,
+ // au: MM_MODEM_PROPERTY_BANDS
+}
+
+void CellularCapabilityUniversal::OnDBusPropertiesChanged(
+ const string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const vector<string> &invalidated_properties) {
+ SLOG(this, 3) << __func__ << "(" << interface << ")";
+ if (interface == MM_DBUS_INTERFACE_MODEM) {
+ OnModemPropertiesChanged(changed_properties, invalidated_properties);
+ }
+ if (interface == MM_DBUS_INTERFACE_MODEM_MODEM3GPP) {
+ OnModem3GPPPropertiesChanged(changed_properties, invalidated_properties);
+ }
+ if (interface == MM_DBUS_INTERFACE_SIM) {
+ OnSimPropertiesChanged(changed_properties, invalidated_properties);
+ }
+}
+
+bool CellularCapabilityUniversal::RetriableConnectError(
+ const Error &error) const {
+ if (error.type() == Error::kInvalidApn)
+ return true;
+
+ // ModemManager does not ever return kInvalidApn for an E362 modem (with
+ // firmware version 1.41) supported by the Novatel LTE plugin.
+ if ((cellular()->mm_plugin() == kNovatelLTEMMPlugin) &&
+ (error.type() == Error::kOperationFailed)) {
+ return true;
+ }
+ return false;
+}
+
+void CellularCapabilityUniversal::OnNetworkModeSignal(uint32_t /*mode*/) {
+ // TODO(petkov): Implement this.
+ NOTIMPLEMENTED();
+}
+
+bool CellularCapabilityUniversal::IsValidSimPath(const string &sim_path) const {
+ return !sim_path.empty() && sim_path != kRootPath;
+}
+
+string CellularCapabilityUniversal::NormalizeMdn(const string &mdn) const {
+ string normalized_mdn;
+ for (size_t i = 0; i < mdn.size(); ++i) {
+ if (IsAsciiDigit(mdn[i]))
+ normalized_mdn += mdn[i];
+ }
+ return normalized_mdn;
+}
+
+void CellularCapabilityUniversal::OnSimPathChanged(
+ const string &sim_path) {
+ if (sim_path == sim_path_)
+ return;
+
+ mm1::SimProxyInterface *proxy = nullptr;
+ if (IsValidSimPath(sim_path))
+ proxy = proxy_factory()->CreateSimProxy(sim_path,
+ cellular()->dbus_owner());
+ sim_path_ = sim_path;
+ sim_proxy_.reset(proxy);
+
+ if (!IsValidSimPath(sim_path)) {
+ // Clear all data about the sim
+ cellular()->set_imsi("");
+ spn_ = "";
+ cellular()->set_sim_present(false);
+ OnSimIdentifierChanged("");
+ OnOperatorIdChanged("");
+ cellular()->home_provider_info()->Reset();
+ } else {
+ cellular()->set_sim_present(true);
+ std::unique_ptr<DBusPropertiesProxyInterface> properties_proxy(
+ proxy_factory()->CreateDBusPropertiesProxy(sim_path,
+ cellular()->dbus_owner()));
+ // TODO(jglasgow): convert to async interface
+ DBusPropertiesMap properties(
+ properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
+ OnSimPropertiesChanged(properties, vector<string>());
+ }
+}
+
+void CellularCapabilityUniversal::OnSupportedCapabilitesChanged(
+ const vector<uint32_t> &supported_capabilities) {
+ supported_capabilities_ = supported_capabilities;
+}
+
+void CellularCapabilityUniversal::OnModemCurrentCapabilitiesChanged(
+ uint32_t current_capabilities) {
+ current_capabilities_ = current_capabilities;
+
+ // Only allow network scan when the modem's current capabilities support
+ // GSM/UMTS.
+ //
+ // TODO(benchan): We should consider having the modem plugins in ModemManager
+ // reporting whether network scan is supported.
+ cellular()->set_scanning_supported(
+ (current_capabilities & MM_MODEM_CAPABILITY_GSM_UMTS) != 0);
+}
+
+void CellularCapabilityUniversal::OnMdnChanged(
+ const string &mdn) {
+ cellular()->set_mdn(NormalizeMdn(mdn));
+ UpdatePendingActivationState();
+}
+
+void CellularCapabilityUniversal::OnModemRevisionChanged(
+ const string &revision) {
+ cellular()->set_firmware_revision(revision);
+}
+
+void CellularCapabilityUniversal::OnModemStateChanged(
+ Cellular::ModemState state) {
+ SLOG(this, 3) << __func__ << ": " << Cellular::GetModemStateString(state);
+
+ if (state == Cellular::kModemStateConnected) {
+ // This assumes that ModemManager updates the Bearers list and the Bearer
+ // properties before changing Modem state to Connected.
+ SLOG(this, 2) << "Update active bearer.";
+ UpdateActiveBearer();
+ }
+
+ cellular()->OnModemStateChanged(state);
+ // TODO(armansito): Move the deferred enable logic to Cellular
+ // (See crbug.com/279499).
+ if (!deferred_enable_modem_callback_.is_null() &&
+ state == Cellular::kModemStateDisabled) {
+ SLOG(this, 2) << "Enabling modem after deferring.";
+ deferred_enable_modem_callback_.Run();
+ deferred_enable_modem_callback_.Reset();
+ }
+}
+
+void CellularCapabilityUniversal::OnAccessTechnologiesChanged(
+ uint32_t access_technologies) {
+ if (access_technologies_ != access_technologies) {
+ const string old_type_string(GetTypeString());
+ access_technologies_ = access_technologies;
+ const string new_type_string(GetTypeString());
+ if (new_type_string != old_type_string) {
+ // TODO(jglasgow): address layering violation of emitting change
+ // signal here for a property owned by Cellular.
+ cellular()->adaptor()->EmitStringChanged(
+ kTechnologyFamilyProperty, new_type_string);
+ }
+ if (cellular()->service().get()) {
+ cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
+ }
+ }
+}
+
+void CellularCapabilityUniversal::OnSupportedModesChanged(
+ const vector<ModemModes> &supported_modes) {
+ supported_modes_ = supported_modes;
+}
+
+void CellularCapabilityUniversal::OnCurrentModesChanged(
+ const ModemModes ¤t_modes) {
+ current_modes_ = current_modes;
+}
+
+void CellularCapabilityUniversal::OnBearersChanged(
+ const RpcIdentifiers &bearers) {
+ bearer_paths_ = bearers;
+}
+
+void CellularCapabilityUniversal::OnLockRetriesChanged(
+ const LockRetryData &lock_retries) {
+ SLOG(this, 3) << __func__;
+
+ // Look for the retries left for the current lock. Try the obtain the count
+ // that matches the current count. If no count for the current lock is
+ // available, report the first one in the dictionary.
+ LockRetryData::const_iterator it =
+ lock_retries.find(sim_lock_status_.lock_type);
+ if (it == lock_retries.end())
+ it = lock_retries.begin();
+ if (it != lock_retries.end())
+ sim_lock_status_.retries_left = it->second;
+ else
+ // Unknown, use 999
+ sim_lock_status_.retries_left = 999;
+}
+
+void CellularCapabilityUniversal::OnLockTypeChanged(
+ MMModemLock lock_type) {
+ SLOG(this, 3) << __func__ << ": " << lock_type;
+ sim_lock_status_.lock_type = lock_type;
+
+ // If the SIM is in a locked state |sim_lock_status_.enabled| might be false.
+ // This is because the corresponding property 'EnabledFacilityLocks' is on
+ // the 3GPP interface and the 3GPP interface is not available while the Modem
+ // is in the 'LOCKED' state.
+ if (lock_type != MM_MODEM_LOCK_NONE &&
+ lock_type != MM_MODEM_LOCK_UNKNOWN &&
+ !sim_lock_status_.enabled)
+ sim_lock_status_.enabled = true;
+}
+
+void CellularCapabilityUniversal::OnSimLockStatusChanged() {
+ SLOG(this, 3) << __func__;
+ cellular()->adaptor()->EmitKeyValueStoreChanged(
+ kSIMLockStatusProperty, SimLockStatusToProperty(nullptr));
+
+ // If the SIM is currently unlocked, assume that we need to refresh
+ // carrier information, since a locked SIM prevents shill from obtaining
+ // the necessary data to establish a connection later (e.g. IMSI).
+ if (IsValidSimPath(sim_path_) &&
+ (sim_lock_status_.lock_type == MM_MODEM_LOCK_NONE ||
+ sim_lock_status_.lock_type == MM_MODEM_LOCK_UNKNOWN)) {
+ std::unique_ptr<DBusPropertiesProxyInterface> properties_proxy(
+ proxy_factory()->CreateDBusPropertiesProxy(sim_path_,
+ cellular()->dbus_owner()));
+ DBusPropertiesMap properties(
+ properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
+ OnSimPropertiesChanged(properties, vector<string>());
+ }
+}
+
+void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
+ const DBusPropertiesMap &properties,
+ const vector<string> &/* invalidated_properties */) {
+ SLOG(this, 3) << __func__;
+ uint32_t uint_value;
+ string imei;
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_MODEM3GPP_PROPERTY_IMEI,
+ &imei))
+ cellular()->set_imei(imei);
+
+ // Handle registration state changes as a single change
+ Stringmap::const_iterator it;
+ string operator_code;
+ string operator_name;
+ it = serving_operator_.find(kOperatorCodeKey);
+ if (it != serving_operator_.end())
+ operator_code = it->second;
+ it = serving_operator_.find(kOperatorNameKey);
+ if (it != serving_operator_.end())
+ operator_name = it->second;
+
+ MMModem3gppRegistrationState state = registration_state_;
+ bool registration_changed = false;
+ if (DBusProperties::GetUint32(properties,
+ MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE,
+ &uint_value)) {
+ state = static_cast<MMModem3gppRegistrationState>(uint_value);
+ registration_changed = true;
+ }
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_MODEM3GPP_PROPERTY_OPERATORCODE,
+ &operator_code))
+ registration_changed = true;
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_MODEM3GPP_PROPERTY_OPERATORNAME,
+ &operator_name))
+ registration_changed = true;
+ if (registration_changed)
+ On3GPPRegistrationChanged(state, operator_code, operator_name);
+ if (DBusProperties::GetUint32(properties,
+ MM_MODEM_MODEM3GPP_PROPERTY_SUBSCRIPTIONSTATE,
+ &uint_value))
+ On3GPPSubscriptionStateChanged(
+ static_cast<MMModem3gppSubscriptionState>(uint_value));
+
+ uint32_t subscription_state;
+ CellularServiceRefPtr service = cellular()->service();
+ if (service.get() &&
+ DBusProperties::GetUint32(properties,
+ MM_MODEM_MODEM3GPP_PROPERTY_SUBSCRIPTIONSTATE,
+ &subscription_state)) {
+ SLOG(this, 3) << __func__ << ": Subscription state = "
+ << subscription_state;
+ service->out_of_credits_detector()->NotifySubscriptionStateChanged(
+ subscription_state);
+ }
+
+ uint32_t locks = 0;
+ if (DBusProperties::GetUint32(
+ properties, MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS,
+ &locks))
+ OnFacilityLocksChanged(locks);
+}
+
+void CellularCapabilityUniversal::On3GPPRegistrationChanged(
+ MMModem3gppRegistrationState state,
+ const string &operator_code,
+ const string &operator_name) {
+ SLOG(this, 3) << __func__ << ": regstate=" << state
+ << ", opercode=" << operator_code
+ << ", opername=" << operator_name;
+
+ // While the modem is connected, if the state changed from a registered state
+ // to a non registered state, defer the state change by 15 seconds.
+ if (cellular()->modem_state() == Cellular::kModemStateConnected &&
+ IsRegistered() && !IsRegisteredState(state)) {
+ if (!registration_dropped_update_callback_.IsCancelled()) {
+ LOG(WARNING) << "Modem reported consecutive 3GPP registration drops. "
+ << "Ignoring earlier notifications.";
+ registration_dropped_update_callback_.Cancel();
+ } else {
+ // This is not a repeated post. So, count this instance of delayed drop
+ // posted.
+ modem_info()->metrics()->Notify3GPPRegistrationDelayedDropPosted();
+ }
+ SLOG(this, 2) << "Posted deferred registration state update";
+ registration_dropped_update_callback_.Reset(
+ Bind(&CellularCapabilityUniversal::Handle3GPPRegistrationChange,
+ weak_ptr_factory_.GetWeakPtr(),
+ state,
+ operator_code,
+ operator_name));
+ cellular()->dispatcher()->PostDelayedTask(
+ registration_dropped_update_callback_.callback(),
+ registration_dropped_update_timeout_milliseconds_);
+ } else {
+ if (!registration_dropped_update_callback_.IsCancelled()) {
+ SLOG(this, 2) << "Cancelled a deferred registration state update";
+ registration_dropped_update_callback_.Cancel();
+ // If we cancelled the callback here, it means we had flaky network for a
+ // small duration.
+ modem_info()->metrics()->Notify3GPPRegistrationDelayedDropCanceled();
+ }
+ Handle3GPPRegistrationChange(state, operator_code, operator_name);
+ }
+}
+
+void CellularCapabilityUniversal::Handle3GPPRegistrationChange(
+ MMModem3gppRegistrationState updated_state,
+ string updated_operator_code,
+ string updated_operator_name) {
+ // A finished callback does not qualify as a canceled callback.
+ // We test for a canceled callback to check for outstanding callbacks.
+ // So, explicitly cancel the callback here.
+ registration_dropped_update_callback_.Cancel();
+
+ SLOG(this, 3) << __func__ << ": regstate=" << updated_state
+ << ", opercode=" << updated_operator_code
+ << ", opername=" << updated_operator_name;
+
+ registration_state_ = updated_state;
+ serving_operator_[kOperatorCodeKey] = updated_operator_code;
+ serving_operator_[kOperatorNameKey] = updated_operator_name;
+ cellular()->serving_operator_info()->UpdateMCCMNC(updated_operator_code);
+ cellular()->serving_operator_info()->UpdateOperatorName(
+ updated_operator_name);
+
+ cellular()->HandleNewRegistrationState();
+
+ // If the modem registered with the network and the current ICCID is pending
+ // activation, then reset the modem.
+ UpdatePendingActivationState();
+}
+
+void CellularCapabilityUniversal::On3GPPSubscriptionStateChanged(
+ MMModem3gppSubscriptionState updated_state) {
+ SLOG(this, 3) << __func__ << ": Updated subscription state = "
+ << updated_state;
+
+ // A one-to-one enum mapping.
+ SubscriptionState new_subscription_state;
+ switch (updated_state) {
+ case MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN:
+ new_subscription_state = kSubscriptionStateUnknown;
+ break;
+ case MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED:
+ new_subscription_state = kSubscriptionStateProvisioned;
+ break;
+ case MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED:
+ new_subscription_state = kSubscriptionStateUnprovisioned;
+ break;
+ case MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA:
+ new_subscription_state = kSubscriptionStateOutOfData;
+ break;
+ default:
+ LOG(ERROR) << "Unrecognized MMModem3gppSubscriptionState: "
+ << updated_state;
+ new_subscription_state = kSubscriptionStateUnknown;
+ return;
+ }
+ if (new_subscription_state == subscription_state_)
+ return;
+
+ subscription_state_ = new_subscription_state;
+
+ UpdateServiceActivationState();
+ UpdatePendingActivationState();
+}
+
+void CellularCapabilityUniversal::OnModemStateChangedSignal(
+ int32_t old_state, int32_t new_state, uint32_t reason) {
+ Cellular::ModemState old_modem_state =
+ static_cast<Cellular::ModemState>(old_state);
+ Cellular::ModemState new_modem_state =
+ static_cast<Cellular::ModemState>(new_state);
+ SLOG(this, 3) << __func__ << "("
+ << Cellular::GetModemStateString(old_modem_state)
+ << ", "
+ << Cellular::GetModemStateString(new_modem_state)
+ << ", "
+ << reason << ")";
+}
+
+void CellularCapabilityUniversal::OnSignalQualityChanged(uint32_t quality) {
+ cellular()->HandleNewSignalQuality(quality);
+}
+
+void CellularCapabilityUniversal::OnFacilityLocksChanged(uint32_t locks) {
+ bool sim_enabled = !!(locks & MM_MODEM_3GPP_FACILITY_SIM);
+ if (sim_lock_status_.enabled != sim_enabled) {
+ sim_lock_status_.enabled = sim_enabled;
+ OnSimLockStatusChanged();
+ }
+}
+
+void CellularCapabilityUniversal::OnSimPropertiesChanged(
+ const DBusPropertiesMap &props,
+ const vector<string> &/* invalidated_properties */) {
+ SLOG(this, 3) << __func__;
+ string value;
+ if (DBusProperties::GetString(props, MM_SIM_PROPERTY_SIMIDENTIFIER, &value))
+ OnSimIdentifierChanged(value);
+ if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORIDENTIFIER,
+ &value))
+ OnOperatorIdChanged(value);
+ if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORNAME, &value))
+ OnSpnChanged(value);
+ if (DBusProperties::GetString(props, MM_SIM_PROPERTY_IMSI, &value)) {
+ cellular()->set_imsi(value);
+ cellular()->home_provider_info()->UpdateIMSI(value);
+ // We do not obtain IMSI OTA right now. Provide the value from the SIM to
+ // serving operator as well, to aid in MVNO identification.
+ cellular()->serving_operator_info()->UpdateIMSI(value);
+ }
+}
+
+void CellularCapabilityUniversal::OnSpnChanged(const std::string &spn) {
+ spn_ = spn;
+ cellular()->home_provider_info()->UpdateOperatorName(spn);
+}
+
+void CellularCapabilityUniversal::OnSimIdentifierChanged(const string &id) {
+ cellular()->set_sim_identifier(id);
+ cellular()->home_provider_info()->UpdateICCID(id);
+ // Provide ICCID to serving operator as well to aid in MVNO identification.
+ cellular()->serving_operator_info()->UpdateICCID(id);
+ UpdatePendingActivationState();
+}
+
+void CellularCapabilityUniversal::OnOperatorIdChanged(
+ const string &operator_id) {
+ SLOG(this, 2) << "Operator ID = '" << operator_id << "'";
+ cellular()->home_provider_info()->UpdateMCCMNC(operator_id);
+}
+
+OutOfCreditsDetector::OOCType
+CellularCapabilityUniversal::GetOutOfCreditsDetectionType() const {
+ if (cellular()->mm_plugin() == kAltairLTEMMPlugin) {
+ return OutOfCreditsDetector::OOCTypeSubscriptionState;
+ } else {
+ return OutOfCreditsDetector::OOCTypeNone;
+ }
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_universal.h b/cellular/cellular_capability_universal.h
new file mode 100644
index 0000000..7c5a56c
--- /dev/null
+++ b/cellular/cellular_capability_universal.h
@@ -0,0 +1,438 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_CAPABILITY_UNIVERSAL_H_
+#define SHILL_CELLULAR_CELLULAR_CAPABILITY_UNIVERSAL_H_
+
+#include <deque>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/memory/weak_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+#include <ModemManager/ModemManager.h>
+
+#include "shill/accessor_interface.h"
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_bearer.h"
+#include "shill/cellular/cellular_capability.h"
+#include "shill/cellular/mm1_modem_modem3gpp_proxy_interface.h"
+#include "shill/cellular/mm1_modem_proxy_interface.h"
+#include "shill/cellular/mm1_modem_simple_proxy_interface.h"
+#include "shill/cellular/mm1_sim_proxy_interface.h"
+#include "shill/cellular/out_of_credits_detector.h"
+
+struct mobile_provider;
+
+namespace shill {
+
+class ModemInfo;
+
+// CellularCapabilityUniversal handles modems using the
+// org.chromium.ModemManager1 DBUS interface. This class is used for
+// all types of modems, i.e. CDMA, GSM, and LTE modems.
+class CellularCapabilityUniversal : public CellularCapability {
+ public:
+ typedef std::vector<DBusPropertiesMap> ScanResults;
+ typedef DBusPropertiesMap ScanResult;
+ typedef std::map<uint32_t, uint32_t> LockRetryData;
+
+ // Constants used in connect method call. Make available to test matchers.
+ // TODO(jglasgow): Generate from modem manager into
+ // ModemManager-names.h.
+ // See http://crbug.com/212909.
+ static const char kConnectPin[];
+ static const char kConnectOperatorId[];
+ static const char kConnectApn[];
+ static const char kConnectIPType[];
+ static const char kConnectUser[];
+ static const char kConnectPassword[];
+ static const char kConnectNumber[];
+ static const char kConnectAllowRoaming[];
+ static const char kConnectRMProtocol[];
+
+ CellularCapabilityUniversal(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info);
+ ~CellularCapabilityUniversal() override;
+
+ // Inherited from CellularCapability.
+ std::string GetTypeString() const override;
+ void OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties) override;
+ // Checks the modem state. If the state is kModemStateDisabled, then the
+ // modem is enabled. Otherwise, the enable command is buffered until the
+ // modem becomes disabled. ModemManager rejects the enable command if the
+ // modem is not disabled, for example, if it is initializing instead.
+ void StartModem(Error *error, const ResultCallback &callback) override;
+ void StopModem(Error *error, const ResultCallback &callback) override;
+ void Reset(Error *error, const ResultCallback &callback) override;
+ bool AreProxiesInitialized() const override;
+ bool IsServiceActivationRequired() const override;
+ void CompleteActivation(Error *error) override;
+ void Scan(Error *error, const ResultStringmapsCallback &callback) override;
+ void RegisterOnNetwork(const std::string &network_id,
+ Error *error,
+ const ResultCallback &callback) override;
+ bool IsRegistered() const override;
+ void SetUnregistered(bool searching) override;
+ void OnServiceCreated() override;
+ std::string GetNetworkTechnologyString() const override;
+ std::string GetRoamingStateString() const override;
+ bool AllowRoaming() override;
+ void GetSignalQuality() override;
+ void SetupConnectProperties(DBusPropertiesMap *properties) override;
+ void Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback) override;
+ void Disconnect(Error *error, const ResultCallback &callback) override;
+ CellularBearer *GetActiveBearer() const override;
+ void RequirePIN(const std::string &pin,
+ bool require,
+ Error *error,
+ const ResultCallback &callback) override;
+ void EnterPIN(const std::string &pin,
+ Error *error,
+ const ResultCallback &callback) override;
+ void UnblockPIN(const std::string &unblock_code,
+ const std::string &pin,
+ Error *error,
+ const ResultCallback &callback) override;
+ void ChangePIN(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error,
+ const ResultCallback &callback) override;
+
+ virtual void GetProperties();
+ virtual void Register(const ResultCallback &callback);
+
+ protected:
+ virtual void InitProxies();
+ virtual void ReleaseProxies();
+
+ // Updates the |sim_path_| variable and creates a new proxy to the
+ // DBUS ModemManager1.Sim interface.
+ // TODO(armansito): Put this method in a 3GPP-only subclass.
+ virtual void OnSimPathChanged(const std::string &sim_path);
+
+ // Updates the online payment portal information, if any, for the cellular
+ // provider.
+ void UpdateServiceOLP() override;
+
+ // Post-payment activation handlers.
+ virtual void UpdatePendingActivationState();
+
+ // Returns the operator-specific form of |mdn|, which is passed to the online
+ // payment portal of a cellular operator.
+ std::string GetMdnForOLP(const MobileOperatorInfo *operator_info) const;
+
+ private:
+ struct ModemModes {
+ ModemModes()
+ : allowed_modes(MM_MODEM_MODE_NONE),
+ preferred_mode(MM_MODEM_MODE_NONE) {}
+
+ ModemModes(uint32_t allowed, MMModemMode preferred)
+ : allowed_modes(allowed),
+ preferred_mode(preferred) {}
+
+ uint32_t allowed_modes; // Bits based on MMModemMode.
+ MMModemMode preferred_mode; // A single MMModemMode bit.
+ };
+
+ // Constants used in scan results. Make available to unit tests.
+ // TODO(jglasgow): Generate from modem manager into ModemManager-names.h.
+ // See http://crbug.com/212909.
+ static const char kStatusProperty[];
+ static const char kOperatorLongProperty[];
+ static const char kOperatorShortProperty[];
+ static const char kOperatorCodeProperty[];
+ static const char kOperatorAccessTechnologyProperty[];
+
+ // Plugin strings via ModemManager.
+ static const char kAltairLTEMMPlugin[];
+ static const char kNovatelLTEMMPlugin[];
+
+ static const int64_t kActivationRegistrationTimeoutMilliseconds;
+ static const int64_t kEnterPinTimeoutMilliseconds;
+ static const int64_t kRegistrationDroppedUpdateTimeoutMilliseconds;
+ static const int kSetPowerStateTimeoutMilliseconds;
+
+
+ // Root path. The SIM path is reported by ModemManager to be the root path
+ // when no SIM is present.
+ static const char kRootPath[];
+
+ friend class CellularTest;
+ friend class CellularCapabilityTest;
+ friend class CellularCapabilityUniversalTest;
+ friend class CellularCapabilityUniversalCDMATest;
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, PropertiesChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, AllowRoaming);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ ActivationWaitForRegisterTimeout);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, Connect);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, ConnectApns);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, DisconnectNoProxy);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ DisconnectWithDeferredCallback);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, ExtractPcoValue);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, GetMdnForOLP);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ GetNetworkTechnologyStringOnE362);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ GetOutOfCreditsDetectionType);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, GetTypeString);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, IsMdnValid);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, IsRegistered);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, IsServiceActivationRequired);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, IsValidSimPath);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, NormalizeMdn);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, OnLockRetriesChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, OnLockTypeChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ OnModemCurrentCapabilitiesChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, OnSimLockPropertiesChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, PropertiesChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, Reset);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, Scan);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, ScanFailure);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, SimLockStatusChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, SimLockStatusToProperty);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, SimPathChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, SimPropertiesChanged);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StartModem);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StartModemFailure);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StartModemInWrongState);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ StartModemWithDeferredEnableFailure);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StopModem);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StopModemAltair);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ StopModemAltairDeleteBearerFailure);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StopModemAltairNotConnected);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, StopModemConnected);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, TerminationAction);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ TerminationActionRemovedByStopModem);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateActiveBearer);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdatePendingActivationState);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdateRegistrationState);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdateRegistrationStateModemNotConnected);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdateServiceActivationState);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateServiceOLP);
+ FRIEND_TEST(CellularCapabilityUniversalTimerTest, CompleteActivation);
+ FRIEND_TEST(CellularTest, EnableTrafficMonitor);
+ FRIEND_TEST(CellularTest,
+ HandleNewRegistrationStateForServiceRequiringActivation);
+ FRIEND_TEST(CellularTest, ModemStateChangeLostRegistration);
+ FRIEND_TEST(CellularTest, OnPPPDied);
+
+ // SimLockStatus represents the fields in the Cellular.SIMLockStatus
+ // DBUS property of the shill device.
+ struct SimLockStatus {
+ public:
+ SimLockStatus() : enabled(false),
+ lock_type(MM_MODEM_LOCK_UNKNOWN),
+ retries_left(0) {}
+
+ bool enabled;
+ MMModemLock lock_type;
+ uint32_t retries_left;
+ };
+
+ // SubscriptionState represents the provisioned state of SIM. It is used
+ // currently by activation logic for LTE to determine if activation process is
+ // complete.
+ enum SubscriptionState {
+ kSubscriptionStateUnknown = 0,
+ kSubscriptionStateUnprovisioned = 1,
+ kSubscriptionStateProvisioned = 2,
+ kSubscriptionStateOutOfData = 3
+ };
+
+ // Methods used in starting a modem
+ void EnableModem(bool deferralbe,
+ Error *error,
+ const ResultCallback& callback);
+ void EnableModemCompleted(bool deferrable,
+ const ResultCallback &callback,
+ const Error &error);
+
+ // Methods used in stopping a modem
+ void Stop_DeleteActiveBearer(const ResultCallback &callback);
+ void Stop_DeleteActiveBearerCompleted(const ResultCallback &callback,
+ const Error &error);
+ void Stop_Disable(const ResultCallback &callback);
+ void Stop_DisableCompleted(const ResultCallback &callback,
+ const Error &error);
+ void Stop_PowerDown(const ResultCallback &callback);
+ void Stop_PowerDownCompleted(const ResultCallback &callback,
+ const Error &error);
+
+ // Updates |active_bearer_| to match the currently active bearer.
+ void UpdateActiveBearer();
+
+ Stringmap ParseScanResult(const ScanResult &result);
+
+ KeyValueStore SimLockStatusToProperty(Error *error);
+
+ void SetupApnTryList();
+ void FillConnectPropertyMap(DBusPropertiesMap *properties);
+
+ void HelpRegisterConstDerivedKeyValueStore(
+ const std::string &name,
+ KeyValueStore(CellularCapabilityUniversal::*get)(Error *error));
+
+ // Returns true if a connect error should be retried. This function
+ // abstracts modem specific behavior for modems which do a lousy job
+ // of returning specific errors on connect failures.
+ bool RetriableConnectError(const Error &error) const;
+
+ // Signal callbacks
+ void OnNetworkModeSignal(uint32_t mode);
+ void OnModemStateChangedSignal(int32_t old_state,
+ int32_t new_state,
+ uint32_t reason);
+
+ // Property Change notification handlers
+ void OnModemPropertiesChanged(
+ const DBusPropertiesMap &properties,
+ const std::vector<std::string> &invalidated_properties);
+
+ void OnSignalQualityChanged(uint32_t quality);
+
+ void OnSupportedCapabilitesChanged(
+ const std::vector<uint32_t> &supported_capabilities);
+ void OnModemCurrentCapabilitiesChanged(uint32_t current_capabilities);
+ void OnMdnChanged(const std::string &mdn);
+ void OnModemRevisionChanged(const std::string &revision);
+ void OnModemStateChanged(Cellular::ModemState state);
+ void OnAccessTechnologiesChanged(uint32_t access_technologies);
+ void OnSupportedModesChanged(const std::vector<ModemModes> &supported_modes);
+ void OnCurrentModesChanged(const ModemModes ¤t_modes);
+ void OnBearersChanged(const RpcIdentifiers &bearers);
+ void OnLockRetriesChanged(const LockRetryData &lock_retries);
+ void OnLockTypeChanged(MMModemLock unlock_required);
+ void OnSimLockStatusChanged();
+
+ // Returns false if the MDN is empty or if the MDN consists of all 0s.
+ bool IsMdnValid() const;
+
+ // 3GPP property change handlers
+ virtual void OnModem3GPPPropertiesChanged(
+ const DBusPropertiesMap &properties,
+ const std::vector<std::string> &invalidated_properties);
+ void On3GPPRegistrationChanged(MMModem3gppRegistrationState state,
+ const std::string &operator_code,
+ const std::string &operator_name);
+ void Handle3GPPRegistrationChange(
+ MMModem3gppRegistrationState updated_state,
+ std::string updated_operator_code,
+ std::string updated_operator_name);
+ void On3GPPSubscriptionStateChanged(MMModem3gppSubscriptionState state);
+ void OnFacilityLocksChanged(uint32_t locks);
+
+ // SIM property change handlers
+ // TODO(armansito): Put these methods in a 3GPP-only subclass.
+ void OnSimPropertiesChanged(
+ const DBusPropertiesMap &props,
+ const std::vector<std::string> &invalidated_properties);
+ void OnSpnChanged(const std::string &spn);
+ void OnSimIdentifierChanged(const std::string &id);
+ void OnOperatorIdChanged(const std::string &operator_id);
+ void OnOperatorNameChanged(const std::string &operator_name);
+
+ // Method callbacks
+ void OnRegisterReply(const ResultCallback &callback,
+ const Error &error);
+ void OnResetReply(const ResultCallback &callback, const Error &error);
+ void OnScanReply(const ResultStringmapsCallback &callback,
+ const ScanResults &results,
+ const Error &error);
+ void OnConnectReply(const ResultCallback &callback,
+ const DBus::Path &bearer,
+ const Error &error);
+
+ // Returns true, if |sim_path| constitutes a valid SIM path. Currently, a
+ // path is accepted to be valid, as long as it is not equal to one of ""
+ // and "/".
+ bool IsValidSimPath(const std::string &sim_path) const;
+
+ // Returns the normalized version of |mdn| by keeping only digits in |mdn|
+ // and removing other non-digit characters.
+ std::string NormalizeMdn(const std::string &mdn) const;
+
+ // Post-payment activation handlers.
+ void ResetAfterActivation();
+ void UpdateServiceActivationState();
+ void OnResetAfterActivationReply(const Error &error);
+
+ static bool IsRegisteredState(MMModem3gppRegistrationState state);
+
+ // Returns the out-of-credits detection algorithm to be used on this modem.
+ OutOfCreditsDetector::OOCType GetOutOfCreditsDetectionType() const;
+
+ // For unit tests.
+ void set_active_bearer(CellularBearer *bearer) {
+ active_bearer_.reset(bearer); // Takes ownership
+ }
+
+ std::unique_ptr<mm1::ModemModem3gppProxyInterface> modem_3gpp_proxy_;
+ std::unique_ptr<mm1::ModemProxyInterface> modem_proxy_;
+ std::unique_ptr<mm1::ModemSimpleProxyInterface> modem_simple_proxy_;
+ std::unique_ptr<mm1::SimProxyInterface> sim_proxy_;
+ // Used to enrich information about the network operator in |ParseScanResult|.
+ // TODO(pprabhu) Instead instantiate a local |MobileOperatorInfo| instance
+ // once the context has been separated out. (crbug.com/363874)
+ std::unique_ptr<MobileOperatorInfo> mobile_operator_info_;
+
+ base::WeakPtrFactory<CellularCapabilityUniversal> weak_ptr_factory_;
+
+ MMModem3gppRegistrationState registration_state_;
+
+ // Bits based on MMModemCapabilities
+ std::vector<uint32_t> supported_capabilities_; // Technologies supported
+ uint32_t current_capabilities_; // Technologies supported without a reload
+ uint32_t access_technologies_; // Bits based on MMModemAccessTechnology
+ std::vector<ModemModes> supported_modes_;
+ ModemModes current_modes_;
+
+ Stringmap serving_operator_;
+ std::string spn_;
+ std::string desired_network_;
+
+ // Properties.
+ std::deque<Stringmap> apn_try_list_;
+ bool resetting_;
+ SimLockStatus sim_lock_status_;
+ SubscriptionState subscription_state_;
+ std::string sim_path_;
+ std::unique_ptr<CellularBearer> active_bearer_;
+ RpcIdentifiers bearer_paths_;
+ bool reset_done_;
+
+ // If the modem is not in a state to be enabled when StartModem is called,
+ // enabling is deferred using this callback.
+ base::Closure deferred_enable_modem_callback_;
+
+ // Sometimes flaky cellular network causes the 3GPP registration state to
+ // rapidly change from registered --> searching and back. Delay such updates
+ // a little to smooth over temporary registration loss.
+ base::CancelableClosure registration_dropped_update_callback_;
+ int64_t registration_dropped_update_timeout_milliseconds_;
+
+ DISALLOW_COPY_AND_ASSIGN(CellularCapabilityUniversal);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_CAPABILITY_UNIVERSAL_H_
diff --git a/cellular/cellular_capability_universal_cdma.cc b/cellular/cellular_capability_universal_cdma.cc
new file mode 100644
index 0000000..147c842
--- /dev/null
+++ b/cellular/cellular_capability_universal_cdma.cc
@@ -0,0 +1,534 @@
+// Copyright (c) 2013 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/cellular_capability_universal_cdma.h"
+
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/cellular/cellular_bearer.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/dbus_properties_proxy_interface.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+#include "shill/pending_activation_store.h"
+#include "shill/proxy_factory.h"
+
+#ifdef MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN
+#error "Do not include mm-modem.h"
+#endif
+
+using base::UintToString;
+
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularCapabilityUniversalCDMA *c) {
+ return c->cellular()->GetRpcIdentifier();
+}
+}
+
+namespace {
+
+const char kPhoneNumber[] = "#777";
+const char kPropertyConnectNumber[] = "number";
+
+} // namespace
+
+CellularCapabilityUniversalCDMA::CellularCapabilityUniversalCDMA(
+ Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info)
+ : CellularCapabilityUniversal(cellular, proxy_factory, modem_info),
+ weak_cdma_ptr_factory_(this),
+ activation_state_(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED),
+ cdma_1x_registration_state_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
+ cdma_evdo_registration_state_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
+ nid_(0),
+ sid_(0) {
+ SLOG(this, 2) << "Cellular capability constructed: Universal CDMA";
+ // TODO(armansito): Update PRL for activation over cellular.
+ // See crbug.com/197330.
+}
+
+CellularCapabilityUniversalCDMA::~CellularCapabilityUniversalCDMA() {}
+
+void CellularCapabilityUniversalCDMA::InitProxies() {
+ SLOG(this, 2) << __func__;
+ modem_cdma_proxy_.reset(
+ proxy_factory()->CreateMM1ModemModemCdmaProxy(cellular()->dbus_path(),
+ cellular()->dbus_owner()));
+ modem_cdma_proxy_->set_activation_state_callback(
+ Bind(&CellularCapabilityUniversalCDMA::OnActivationStateChangedSignal,
+ weak_cdma_ptr_factory_.GetWeakPtr()));
+ CellularCapabilityUniversal::InitProxies();
+}
+
+void CellularCapabilityUniversalCDMA::ReleaseProxies() {
+ SLOG(this, 2) << __func__;
+ modem_cdma_proxy_.reset();
+ CellularCapabilityUniversal::ReleaseProxies();
+}
+
+void CellularCapabilityUniversalCDMA::Activate(const string &carrier,
+ Error *error,
+ const ResultCallback &callback) {
+ // Currently activation over the cellular network is not supported using
+ // ModemManager-next. Service activation is currently carried through over
+ // non-cellular networks and only the final step of the OTA activation
+ // procedure ("automatic activation") is performed by this class.
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapabilityUniversalCDMA::CompleteActivation(Error *error) {
+ SLOG(this, 2) << __func__;
+ if (cellular()->state() < Cellular::kStateEnabled) {
+ Error::PopulateAndLog(error, Error::kInvalidArguments,
+ "Unable to activate in state " +
+ Cellular::GetStateString(cellular()->state()));
+ return;
+ }
+ ActivateAutomatic();
+}
+
+void CellularCapabilityUniversalCDMA::ActivateAutomatic() {
+ if (!cellular()->serving_operator_info()->IsMobileNetworkOperatorKnown() ||
+ cellular()->serving_operator_info()->activation_code().empty()) {
+ SLOG(this, 2) << "OTA activation cannot be run in the presence of no "
+ << "activation code.";
+ return;
+ }
+
+ PendingActivationStore::State state =
+ modem_info()->pending_activation_store()->GetActivationState(
+ PendingActivationStore::kIdentifierMEID, cellular()->meid());
+ if (state == PendingActivationStore::kStatePending) {
+ SLOG(this, 2) << "There's already a pending activation. Ignoring.";
+ return;
+ }
+ if (state == PendingActivationStore::kStateActivated) {
+ SLOG(this, 2) << "A call to OTA activation has already completed "
+ << "successfully. Ignoring.";
+ return;
+ }
+
+ // Mark as pending activation, so that shill can recover if anything fails
+ // during OTA activation.
+ modem_info()->pending_activation_store()->SetActivationState(
+ PendingActivationStore::kIdentifierMEID,
+ cellular()->meid(),
+ PendingActivationStore::kStatePending);
+
+ // Initiate OTA activation.
+ ResultCallback activation_callback =
+ Bind(&CellularCapabilityUniversalCDMA::OnActivateReply,
+ weak_cdma_ptr_factory_.GetWeakPtr(),
+ ResultCallback());
+
+ Error error;
+ modem_cdma_proxy_->Activate(
+ cellular()->serving_operator_info()->activation_code(),
+ &error,
+ activation_callback,
+ kTimeoutActivate);
+}
+
+void CellularCapabilityUniversalCDMA::UpdatePendingActivationState() {
+ SLOG(this, 2) << __func__;
+ if (IsActivated()) {
+ SLOG(this, 3) << "CDMA service activated. Clear store.";
+ modem_info()->pending_activation_store()->RemoveEntry(
+ PendingActivationStore::kIdentifierMEID, cellular()->meid());
+ return;
+ }
+ PendingActivationStore::State state =
+ modem_info()->pending_activation_store()->GetActivationState(
+ PendingActivationStore::kIdentifierMEID, cellular()->meid());
+ if (IsActivating() && state != PendingActivationStore::kStateFailureRetry) {
+ SLOG(this, 3) << "OTA activation in progress. Nothing to do.";
+ return;
+ }
+ switch (state) {
+ case PendingActivationStore::kStateFailureRetry:
+ SLOG(this, 3) << "OTA activation failed. Scheduling a retry.";
+ cellular()->dispatcher()->PostTask(
+ Bind(&CellularCapabilityUniversalCDMA::ActivateAutomatic,
+ weak_cdma_ptr_factory_.GetWeakPtr()));
+ break;
+ case PendingActivationStore::kStateActivated:
+ SLOG(this, 3) << "OTA Activation has completed successfully. "
+ << "Waiting for activation state update to finalize.";
+ break;
+ default:
+ break;
+ }
+}
+
+bool CellularCapabilityUniversalCDMA::IsServiceActivationRequired() const {
+ // If there is no online payment portal information, it's safer to assume
+ // the service does not require activation.
+ if (!cellular()->serving_operator_info()->IsMobileNetworkOperatorKnown() ||
+ cellular()->serving_operator_info()->olp_list().empty()) {
+ return false;
+ }
+
+ // We could also use the MDN to determine whether or not the service is
+ // activated, however, the CDMA ActivatonState property is a more absolute
+ // and fine-grained indicator of activation status.
+ return (activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED);
+}
+
+bool CellularCapabilityUniversalCDMA::IsActivated() const {
+ return (activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED);
+}
+
+void CellularCapabilityUniversalCDMA::OnServiceCreated() {
+ SLOG(this, 2) << __func__;
+ cellular()->service()->SetActivationType(
+ CellularService::kActivationTypeOTASP);
+ UpdateServiceActivationStateProperty();
+ HandleNewActivationStatus(MM_CDMA_ACTIVATION_ERROR_NONE);
+ UpdatePendingActivationState();
+}
+
+void CellularCapabilityUniversalCDMA::UpdateServiceActivationStateProperty() {
+ string activation_state;
+ if (IsActivating())
+ activation_state = kActivationStateActivating;
+ else if (IsServiceActivationRequired())
+ activation_state = kActivationStateNotActivated;
+ else
+ activation_state = kActivationStateActivated;
+ cellular()->service()->SetActivationState(activation_state);
+}
+
+void CellularCapabilityUniversalCDMA::UpdateServiceOLP() {
+ SLOG(this, 2) << __func__;
+
+ // In this case, the Home Provider is trivial. All information comes from the
+ // Serving Operator.
+ if (!cellular()->serving_operator_info()->IsMobileNetworkOperatorKnown()) {
+ return;
+ }
+
+ const vector<MobileOperatorInfo::OnlinePortal> &olp_list =
+ cellular()->serving_operator_info()->olp_list();
+ if (olp_list.empty()) {
+ return;
+ }
+
+ if (olp_list.size() > 1) {
+ SLOG(this, 1) << "Found multiple online portals. Choosing the first.";
+ }
+ string post_data = olp_list[0].post_data;
+ ReplaceSubstringsAfterOffset(&post_data, 0, "${esn}", cellular()->esn());
+ ReplaceSubstringsAfterOffset(
+ &post_data, 0, "${mdn}",
+ GetMdnForOLP(cellular()->serving_operator_info()));
+ ReplaceSubstringsAfterOffset(&post_data, 0, "${meid}", cellular()->meid());
+ ReplaceSubstringsAfterOffset(&post_data, 0, "${oem}", "GOG2");
+ cellular()->service()->SetOLP(olp_list[0].url, olp_list[0].method, post_data);
+}
+
+void CellularCapabilityUniversalCDMA::GetProperties() {
+ SLOG(this, 2) << __func__;
+ CellularCapabilityUniversal::GetProperties();
+
+ std::unique_ptr<DBusPropertiesProxyInterface> properties_proxy(
+ proxy_factory()->CreateDBusPropertiesProxy(cellular()->dbus_path(),
+ cellular()->dbus_owner()));
+ DBusPropertiesMap properties(
+ properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM_MODEMCDMA));
+ OnModemCDMAPropertiesChanged(properties, vector<string>());
+}
+
+void CellularCapabilityUniversalCDMA::OnActivationStateChangedSignal(
+ uint32_t activation_state,
+ uint32_t activation_error,
+ const DBusPropertiesMap &status_changes) {
+ SLOG(this, 2) << __func__;
+
+ activation_state_ =
+ static_cast<MMModemCdmaActivationState>(activation_state);
+
+ string value;
+ if (DBusProperties::GetString(status_changes, "mdn", &value))
+ cellular()->set_mdn(value);
+ if (DBusProperties::GetString(status_changes, "min", &value))
+ cellular()->set_min(value);
+
+ SLOG(this, 2) << "Activation state: "
+ << GetActivationStateString(activation_state_);
+
+ HandleNewActivationStatus(activation_error);
+ UpdatePendingActivationState();
+}
+
+void CellularCapabilityUniversalCDMA::OnActivateReply(
+ const ResultCallback &callback,
+ const Error &error) {
+ SLOG(this, 2) << __func__;
+ if (error.IsSuccess()) {
+ LOG(INFO) << "Activation completed successfully.";
+ modem_info()->pending_activation_store()->SetActivationState(
+ PendingActivationStore::kIdentifierMEID,
+ cellular()->meid(),
+ PendingActivationStore::kStateActivated);
+ } else {
+ LOG(ERROR) << "Activation failed with error: " << error;
+ modem_info()->pending_activation_store()->SetActivationState(
+ PendingActivationStore::kIdentifierMEID,
+ cellular()->meid(),
+ PendingActivationStore::kStateFailureRetry);
+ }
+ UpdatePendingActivationState();
+
+ // CellularCapabilityUniversalCDMA::ActivateAutomatic passes a dummy
+ // ResultCallback when it calls Activate on the proxy object, in which case
+ // |callback.is_null()| will return true.
+ if (!callback.is_null())
+ callback.Run(error);
+}
+
+void CellularCapabilityUniversalCDMA::HandleNewActivationStatus(
+ uint32_t error) {
+ SLOG(this, 2) << __func__ << "(" << error << ")";
+ if (!cellular()->service().get()) {
+ LOG(ERROR) << "In " << __func__ << "(): service is null.";
+ return;
+ }
+ SLOG(this, 2) << "Activation State: " << activation_state_;
+ cellular()->service()->SetActivationState(
+ GetActivationStateString(activation_state_));
+ cellular()->service()->set_error(GetActivationErrorString(error));
+ UpdateServiceOLP();
+}
+
+// static
+string CellularCapabilityUniversalCDMA::GetActivationStateString(
+ uint32_t state) {
+ switch (state) {
+ case MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED:
+ return kActivationStateActivated;
+ case MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING:
+ return kActivationStateActivating;
+ case MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED:
+ return kActivationStateNotActivated;
+ case MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED:
+ return kActivationStatePartiallyActivated;
+ default:
+ return kActivationStateUnknown;
+ }
+}
+
+// static
+string CellularCapabilityUniversalCDMA::GetActivationErrorString(
+ uint32_t error) {
+ switch (error) {
+ case MM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE:
+ return kErrorNeedEvdo;
+ case MM_CDMA_ACTIVATION_ERROR_ROAMING:
+ return kErrorNeedHomeNetwork;
+ case MM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT:
+ case MM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED:
+ case MM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED:
+ return kErrorOtaspFailed;
+ case MM_CDMA_ACTIVATION_ERROR_NONE:
+ return "";
+ case MM_CDMA_ACTIVATION_ERROR_NO_SIGNAL:
+ default:
+ return kErrorActivationFailed;
+ }
+}
+
+void CellularCapabilityUniversalCDMA::Register(const ResultCallback &callback) {
+ // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::RegisterOnNetwork(
+ const string &network_id,
+ Error *error,
+ const ResultCallback &callback) {
+ // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+bool CellularCapabilityUniversalCDMA::IsActivating() const {
+ PendingActivationStore::State state =
+ modem_info()->pending_activation_store()->GetActivationState(
+ PendingActivationStore::kIdentifierMEID, cellular()->meid());
+ return (state == PendingActivationStore::kStatePending) ||
+ (state == PendingActivationStore::kStateFailureRetry) ||
+ (activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING);
+}
+
+bool CellularCapabilityUniversalCDMA::IsRegistered() const {
+ return (cdma_1x_registration_state_ !=
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN ||
+ cdma_evdo_registration_state_ !=
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+}
+
+void CellularCapabilityUniversalCDMA::SetUnregistered(bool /*searching*/) {
+ cdma_1x_registration_state_ = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ cdma_evdo_registration_state_ = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+}
+
+void CellularCapabilityUniversalCDMA::SetupConnectProperties(
+ DBusPropertiesMap *properties) {
+ (*properties)[kPropertyConnectNumber].writer().append_string(
+ kPhoneNumber);
+}
+
+void CellularCapabilityUniversalCDMA::RequirePIN(
+ const string &pin, bool require,
+ Error *error, const ResultCallback &callback) {
+ // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::EnterPIN(
+ const string &pin,
+ Error *error,
+ const ResultCallback &callback) {
+ // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::UnblockPIN(
+ const string &unblock_code,
+ const string &pin,
+ Error *error,
+ const ResultCallback &callback) {
+ // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::ChangePIN(
+ const string &old_pin, const string &new_pin,
+ Error *error, const ResultCallback &callback) {
+ // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::Scan(
+ Error *error,
+ const ResultStringmapsCallback &callback) {
+ // TODO(armansito): Remove once 3GPP is implemented in its own class.
+ OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapabilityUniversalCDMA::OnSimPathChanged(
+ const string &sim_path) {
+ // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+string CellularCapabilityUniversalCDMA::GetRoamingStateString() const {
+ uint32_t state = cdma_evdo_registration_state_;
+ if (state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) {
+ state = cdma_1x_registration_state_;
+ }
+ switch (state) {
+ case MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN:
+ case MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED:
+ break;
+ case MM_MODEM_CDMA_REGISTRATION_STATE_HOME:
+ return kRoamingStateHome;
+ case MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING:
+ return kRoamingStateRoaming;
+ default:
+ NOTREACHED();
+ }
+ return kRoamingStateUnknown;
+}
+
+void CellularCapabilityUniversalCDMA::OnDBusPropertiesChanged(
+ const string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const vector<string> &invalidated_properties) {
+ SLOG(this, 2) << __func__ << "(" << interface << ")";
+ if (interface == MM_DBUS_INTERFACE_MODEM_MODEMCDMA) {
+ OnModemCDMAPropertiesChanged(changed_properties, invalidated_properties);
+ } else {
+ CellularCapabilityUniversal::OnDBusPropertiesChanged(
+ interface, changed_properties, invalidated_properties);
+ }
+}
+
+void CellularCapabilityUniversalCDMA::OnModemCDMAPropertiesChanged(
+ const DBusPropertiesMap &properties,
+ const std::vector<std::string> &/*invalidated_properties*/) {
+ SLOG(this, 2) << __func__;
+ string str_value;
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_MODEMCDMA_PROPERTY_MEID,
+ &str_value))
+ cellular()->set_meid(str_value);
+ if (DBusProperties::GetString(properties,
+ MM_MODEM_MODEMCDMA_PROPERTY_ESN,
+ &str_value))
+ cellular()->set_esn(str_value);
+
+ uint32_t sid = sid_;
+ uint32_t nid = nid_;
+ MMModemCdmaRegistrationState state_1x = cdma_1x_registration_state_;
+ MMModemCdmaRegistrationState state_evdo = cdma_evdo_registration_state_;
+ bool registration_changed = false;
+ uint32_t uint_value;
+ if (DBusProperties::GetUint32(
+ properties,
+ MM_MODEM_MODEMCDMA_PROPERTY_CDMA1XREGISTRATIONSTATE,
+ &uint_value)) {
+ state_1x = static_cast<MMModemCdmaRegistrationState>(uint_value);
+ registration_changed = true;
+ }
+ if (DBusProperties::GetUint32(
+ properties,
+ MM_MODEM_MODEMCDMA_PROPERTY_EVDOREGISTRATIONSTATE,
+ &uint_value)) {
+ state_evdo = static_cast<MMModemCdmaRegistrationState>(uint_value);
+ registration_changed = true;
+ }
+ if (DBusProperties::GetUint32(
+ properties,
+ MM_MODEM_MODEMCDMA_PROPERTY_SID,
+ &uint_value)) {
+ sid = uint_value;
+ registration_changed = true;
+ }
+ if (DBusProperties::GetUint32(
+ properties,
+ MM_MODEM_MODEMCDMA_PROPERTY_NID,
+ &uint_value)) {
+ nid = uint_value;
+ registration_changed = true;
+ }
+ if (DBusProperties::GetUint32(
+ properties,
+ MM_MODEM_MODEMCDMA_PROPERTY_ACTIVATIONSTATE,
+ &uint_value)) {
+ activation_state_ = static_cast<MMModemCdmaActivationState>(uint_value);
+ HandleNewActivationStatus(MM_CDMA_ACTIVATION_ERROR_NONE);
+ }
+ if (registration_changed)
+ OnCDMARegistrationChanged(state_1x, state_evdo, sid, nid);
+}
+
+void CellularCapabilityUniversalCDMA::OnCDMARegistrationChanged(
+ MMModemCdmaRegistrationState state_1x,
+ MMModemCdmaRegistrationState state_evdo,
+ uint32_t sid, uint32_t nid) {
+ SLOG(this, 2) << __func__ << ": state_1x=" << state_1x
+ << ", state_evdo=" << state_evdo;
+ cdma_1x_registration_state_ = state_1x;
+ cdma_evdo_registration_state_ = state_evdo;
+ sid_ = sid;
+ nid_ = nid;
+ cellular()->serving_operator_info()->UpdateSID(UintToString(sid));
+ cellular()->serving_operator_info()->UpdateNID(UintToString(nid));
+ cellular()->HandleNewRegistrationState();
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_universal_cdma.h b/cellular/cellular_capability_universal_cdma.h
new file mode 100644
index 0000000..8300a75
--- /dev/null
+++ b/cellular/cellular_capability_universal_cdma.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_CAPABILITY_UNIVERSAL_CDMA_H_
+#define SHILL_CELLULAR_CELLULAR_CAPABILITY_UNIVERSAL_CDMA_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/memory/weak_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_capability_universal.h"
+#include "shill/cellular/mm1_modem_modemcdma_proxy_interface.h"
+
+namespace shill {
+
+class CellularCapabilityUniversalCDMA : public CellularCapabilityUniversal {
+ public:
+ CellularCapabilityUniversalCDMA(Cellular *cellular,
+ ProxyFactory *proxy_factory,
+ ModemInfo *modem_info);
+ ~CellularCapabilityUniversalCDMA() override;
+
+ // Returns true if the service is activated.
+ bool IsActivated() const;
+
+ // Inherited from CellularCapability.
+ void OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties) override;
+ bool IsServiceActivationRequired() const override;
+ bool IsActivating() const override;
+ void Activate(const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback) override;
+ void CompleteActivation(Error *error) override;
+ bool IsRegistered() const override;
+ void SetUnregistered(bool searching) override;
+ void OnServiceCreated() override;
+ std::string GetRoamingStateString() const override;
+ void SetupConnectProperties(DBusPropertiesMap *properties) override;
+
+ // TODO(armansito): Remove once 3GPP is implemented in its own class
+ void Register(const ResultCallback &callback) override;
+ void RegisterOnNetwork(const std::string &network_id, Error *error,
+ const ResultCallback &callback) override;
+ void RequirePIN(const std::string &pin, bool require, Error *error,
+ const ResultCallback &callback) override;
+ void EnterPIN(const std::string &pin, Error *error,
+ const ResultCallback &callback) override;
+ void UnblockPIN(const std::string &unblock_code,
+ const std::string &pin, Error *error,
+ const ResultCallback &callback) override;
+ void ChangePIN(const std::string &old_pin, const std::string &new_pin,
+ Error *error, const ResultCallback &callback) override;
+ void Scan(Error *error,
+ const ResultStringmapsCallback &callback) override;
+ void OnSimPathChanged(const std::string &sim_path) override;
+
+ void GetProperties() override;
+
+ protected:
+ // Inherited from CellularCapabilityUniversal.
+ void InitProxies() override;
+ void ReleaseProxies() override;
+ void UpdateServiceOLP() override;
+
+ // Post-payment activation handlers.
+ void UpdatePendingActivationState() override;
+
+ private:
+ friend class CellularCapabilityUniversalCDMATest;
+ FRIEND_TEST(CellularCapabilityUniversalCDMADispatcherTest,
+ UpdatePendingActivationState);
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, ActivateAutomatic);
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, IsActivating);
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, IsRegistered);
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+ IsServiceActivationRequired);
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+ OnCDMARegistrationChanged);
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, PropertiesChanged);
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, UpdateServiceOLP);
+ FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+ UpdateServiceActivationStateProperty);
+
+ // CDMA property change handlers
+ virtual void OnModemCDMAPropertiesChanged(
+ const DBusPropertiesMap &properties,
+ const std::vector<std::string> &invalidated_properties);
+ void OnCDMARegistrationChanged(MMModemCdmaRegistrationState state_1x,
+ MMModemCdmaRegistrationState state_evdo,
+ uint32_t sid, uint32_t nid);
+
+ // CDMA activation handlers
+ void ActivateAutomatic();
+ void OnActivationStateChangedSignal(uint32_t activation_state,
+ uint32_t activation_error,
+ const DBusPropertiesMap &status_changes);
+ void OnActivateReply(const ResultCallback &callback,
+ const Error &error);
+ void HandleNewActivationStatus(uint32_t error);
+
+ void UpdateServiceActivationStateProperty();
+
+ static std::string GetActivationStateString(uint32_t state);
+ static std::string GetActivationErrorString(uint32_t error);
+
+ std::unique_ptr<mm1::ModemModemCdmaProxyInterface> modem_cdma_proxy_;
+ // TODO(armansito): Should probably call this |weak_ptr_factory_| after
+ // 3gpp refactor
+ base::WeakPtrFactory<CellularCapabilityUniversalCDMA> weak_cdma_ptr_factory_;
+
+ // CDMA ActivationState property.
+ MMModemCdmaActivationState activation_state_;
+
+ MMModemCdmaRegistrationState cdma_1x_registration_state_;
+ MMModemCdmaRegistrationState cdma_evdo_registration_state_;
+
+ uint32_t nid_;
+ uint32_t sid_;
+
+ DISALLOW_COPY_AND_ASSIGN(CellularCapabilityUniversalCDMA);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_CAPABILITY_UNIVERSAL_CDMA_H_
diff --git a/cellular/cellular_capability_universal_cdma_unittest.cc b/cellular/cellular_capability_universal_cdma_unittest.cc
new file mode 100644
index 0000000..02ae2db
--- /dev/null
+++ b/cellular/cellular_capability_universal_cdma_unittest.cc
@@ -0,0 +1,633 @@
+// Copyright (c) 2013 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/cellular_capability_universal_cdma.h"
+
+#include <string>
+#include <vector>
+
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ModemManager/ModemManager.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/mock_cellular_service.h"
+#include "shill/cellular/mock_mm1_modem_modem3gpp_proxy.h"
+#include "shill/cellular/mock_mm1_modem_modemcdma_proxy.h"
+#include "shill/cellular/mock_mm1_modem_proxy.h"
+#include "shill/cellular/mock_mm1_modem_simple_proxy.h"
+#include "shill/cellular/mock_mm1_sim_proxy.h"
+#include "shill/cellular/mock_mobile_operator_info.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_adaptors.h"
+#include "shill/mock_dbus_properties_proxy.h"
+#include "shill/mock_glib.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_metrics.h"
+#include "shill/mock_pending_activation_store.h"
+#include "shill/nice_mock_control.h"
+#include "shill/proxy_factory.h"
+
+using base::StringPrintf;
+using base::UintToString;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using testing::Invoke;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::_;
+
+namespace shill {
+
+class CellularCapabilityUniversalCDMATest : public testing::Test {
+ public:
+ explicit CellularCapabilityUniversalCDMATest(EventDispatcher *dispatcher)
+ : dispatcher_(dispatcher),
+ capability_(nullptr),
+ device_adaptor_(nullptr),
+ modem_info_(nullptr, dispatcher, nullptr, nullptr, nullptr),
+ modem_3gpp_proxy_(new mm1::MockModemModem3gppProxy()),
+ modem_cdma_proxy_(new mm1::MockModemModemCdmaProxy()),
+ modem_proxy_(new mm1::MockModemProxy()),
+ modem_simple_proxy_(new mm1::MockModemSimpleProxy()),
+ sim_proxy_(new mm1::MockSimProxy()),
+ properties_proxy_(new MockDBusPropertiesProxy()),
+ proxy_factory_(this),
+ cellular_(new Cellular(&modem_info_,
+ "",
+ kMachineAddress,
+ 0,
+ Cellular::kTypeUniversalCDMA,
+ "",
+ "",
+ "",
+ &proxy_factory_)),
+ service_(new MockCellularService(&modem_info_,
+ cellular_)),
+ mock_home_provider_info_(nullptr),
+ mock_serving_operator_info_(nullptr) {}
+
+ virtual ~CellularCapabilityUniversalCDMATest() {
+ cellular_->service_ = nullptr;
+ capability_ = nullptr;
+ device_adaptor_ = nullptr;
+ }
+
+ virtual void SetUp() {
+ capability_ = dynamic_cast<CellularCapabilityUniversalCDMA *>(
+ cellular_->capability_.get());
+ device_adaptor_ =
+ dynamic_cast<NiceMock<DeviceMockAdaptor> *>(cellular_->adaptor());
+ cellular_->service_ = service_;
+ }
+
+ virtual void TearDown() {
+ capability_->proxy_factory_ = nullptr;
+ }
+
+ void SetService() {
+ cellular_->service_ = new CellularService(&modem_info_, cellular_);
+ }
+
+ void ClearService() {
+ cellular_->service_ = nullptr;
+ }
+
+ void ReleaseCapabilityProxies() {
+ capability_->ReleaseProxies();
+ }
+
+ void SetCdmaProxy() {
+ capability_->modem_cdma_proxy_.reset(modem_cdma_proxy_.release());
+ }
+
+ void SetSimpleProxy() {
+ capability_->modem_simple_proxy_.reset(modem_simple_proxy_.release());
+ }
+
+ void SetMockMobileOperatorInfoObjects() {
+ CHECK(!mock_home_provider_info_);
+ CHECK(!mock_serving_operator_info_);
+ mock_home_provider_info_ =
+ new MockMobileOperatorInfo(dispatcher_, "HomeProvider");
+ mock_serving_operator_info_ =
+ new MockMobileOperatorInfo(dispatcher_, "ServingOperator");
+ cellular_->set_home_provider_info(mock_home_provider_info_);
+ cellular_->set_serving_operator_info(mock_serving_operator_info_);
+ }
+
+ protected:
+ static const char kEsn[];
+ static const char kMachineAddress[];
+ static const char kMeid[];
+
+ class TestProxyFactory : public ProxyFactory {
+ public:
+ explicit TestProxyFactory(CellularCapabilityUniversalCDMATest *test) :
+ test_(test) {}
+
+ // TODO(armansito): Some of these methods won't be necessary after 3GPP
+ // gets refactored out of CellularCapabilityUniversal.
+ virtual mm1::ModemModem3gppProxyInterface *CreateMM1ModemModem3gppProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->modem_3gpp_proxy_.release();
+ }
+
+ virtual mm1::ModemModemCdmaProxyInterface *CreateMM1ModemModemCdmaProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->modem_cdma_proxy_.release();
+ }
+
+ virtual mm1::ModemProxyInterface *CreateMM1ModemProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->modem_proxy_.release();
+ }
+
+ virtual mm1::ModemSimpleProxyInterface *CreateMM1ModemSimpleProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->modem_simple_proxy_.release();
+ }
+
+ virtual mm1::SimProxyInterface *CreateSimProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->sim_proxy_.release();
+ }
+
+ virtual DBusPropertiesProxyInterface *CreateDBusPropertiesProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->properties_proxy_.release();
+ }
+
+ private:
+ CellularCapabilityUniversalCDMATest *test_;
+ };
+
+ EventDispatcher *dispatcher_;
+ CellularCapabilityUniversalCDMA *capability_;
+ NiceMock<DeviceMockAdaptor> *device_adaptor_;
+ MockModemInfo modem_info_;
+ MockGLib glib_;
+ // TODO(armansito): Remove |modem_3gpp_proxy_| after refactor.
+ unique_ptr<mm1::MockModemModem3gppProxy> modem_3gpp_proxy_;
+ unique_ptr<mm1::MockModemModemCdmaProxy> modem_cdma_proxy_;
+ unique_ptr<mm1::MockModemProxy> modem_proxy_;
+ unique_ptr<mm1::MockModemSimpleProxy> modem_simple_proxy_;
+ unique_ptr<mm1::MockSimProxy> sim_proxy_;
+ unique_ptr<MockDBusPropertiesProxy> properties_proxy_;
+ TestProxyFactory proxy_factory_;
+ CellularRefPtr cellular_;
+ MockCellularService *service_;
+
+ // Set when required and passed to |cellular_|. Owned by |cellular_|.
+ MockMobileOperatorInfo *mock_home_provider_info_;
+ MockMobileOperatorInfo *mock_serving_operator_info_;
+};
+
+// static
+const char CellularCapabilityUniversalCDMATest::kEsn[] = "0000";
+// static
+const char CellularCapabilityUniversalCDMATest::kMachineAddress[] =
+ "TestMachineAddress";
+// static
+const char CellularCapabilityUniversalCDMATest::kMeid[] = "11111111111111";
+
+class CellularCapabilityUniversalCDMAMainTest
+ : public CellularCapabilityUniversalCDMATest {
+ public:
+ CellularCapabilityUniversalCDMAMainTest()
+ : CellularCapabilityUniversalCDMATest(&dispatcher_) {}
+
+ private:
+ EventDispatcher dispatcher_;
+};
+
+class CellularCapabilityUniversalCDMADispatcherTest
+ : public CellularCapabilityUniversalCDMATest {
+ public:
+ CellularCapabilityUniversalCDMADispatcherTest()
+ : CellularCapabilityUniversalCDMATest(nullptr) {}
+};
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, PropertiesChanged) {
+ // Set up mock modem CDMA properties.
+ DBusPropertiesMap modem_cdma_properties;
+ modem_cdma_properties[MM_MODEM_MODEMCDMA_PROPERTY_MEID].
+ writer().append_string(kMeid);
+ modem_cdma_properties[MM_MODEM_MODEMCDMA_PROPERTY_ESN].
+ writer().append_string(kEsn);
+
+ SetUp();
+
+ EXPECT_TRUE(cellular_->meid().empty());
+ EXPECT_TRUE(cellular_->esn().empty());
+
+ // Changing properties on wrong interface will not have an effect
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM,
+ modem_cdma_properties,
+ vector<string>());
+ EXPECT_TRUE(cellular_->meid().empty());
+ EXPECT_TRUE(cellular_->esn().empty());
+
+ // Changing properties on the right interface gets reflected in the
+ // capabilities object
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM_MODEMCDMA,
+ modem_cdma_properties,
+ vector<string>());
+ EXPECT_EQ(kMeid, cellular_->meid());
+ EXPECT_EQ(kEsn, cellular_->esn());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, OnCDMARegistrationChanged) {
+ EXPECT_EQ(0, capability_->sid_);
+ EXPECT_EQ(0, capability_->nid_);
+ EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ capability_->cdma_1x_registration_state_);
+ EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ capability_->cdma_evdo_registration_state_);
+
+ const unsigned kSid = 2;
+ const unsigned kNid = 1;
+ SetMockMobileOperatorInfoObjects();
+ EXPECT_CALL(*mock_serving_operator_info_, UpdateSID(UintToString(kSid)));
+ EXPECT_CALL(*mock_serving_operator_info_, UpdateNID(UintToString(kNid)));
+ capability_->OnCDMARegistrationChanged(
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+ kSid,
+ kNid);
+ EXPECT_EQ(kSid, capability_->sid_);
+ EXPECT_EQ(kNid, capability_->nid_);
+ EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ capability_->cdma_1x_registration_state_);
+ EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+ capability_->cdma_evdo_registration_state_);
+
+ EXPECT_TRUE(capability_->IsRegistered());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, UpdateServiceOLP) {
+ const MobileOperatorInfo::OnlinePortal kOlp {
+ "http://testurl",
+ "POST",
+ "esn=${esn}&mdn=${mdn}&meid=${meid}"};
+ const vector<MobileOperatorInfo::OnlinePortal> kOlpList {kOlp};
+ const string kUuidVzw = "c83d6597-dc91-4d48-a3a7-d86b80123751";
+ const string kUuidFoo = "foo";
+
+ SetMockMobileOperatorInfoObjects();
+ cellular_->set_esn("0");
+ cellular_->set_mdn("10123456789");
+ cellular_->set_meid("4");
+
+
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, olp_list())
+ .WillRepeatedly(ReturnRef(kOlpList));
+ EXPECT_CALL(*mock_serving_operator_info_, uuid())
+ .WillOnce(ReturnRef(kUuidVzw));
+ SetService();
+ capability_->UpdateServiceOLP();
+ // Copy to simplify assertions below.
+ Stringmap vzw_olp = cellular_->service()->olp();
+ EXPECT_EQ("http://testurl", vzw_olp[kPaymentPortalURL]);
+ EXPECT_EQ("POST", vzw_olp[kPaymentPortalMethod]);
+ EXPECT_EQ("esn=0&mdn=0123456789&meid=4",
+ vzw_olp[kPaymentPortalPostData]);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, olp_list())
+ .WillRepeatedly(ReturnRef(kOlpList));
+ EXPECT_CALL(*mock_serving_operator_info_, uuid())
+ .WillOnce(ReturnRef(kUuidFoo));
+ capability_->UpdateServiceOLP();
+ // Copy to simplify assertions below.
+ Stringmap olp = cellular_->service()->olp();
+ EXPECT_EQ("http://testurl", olp[kPaymentPortalURL]);
+ EXPECT_EQ("POST", olp[kPaymentPortalMethod]);
+ EXPECT_EQ("esn=0&mdn=10123456789&meid=4",
+ olp[kPaymentPortalPostData]);
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, ActivateAutomatic) {
+ const string activation_code {"1234"};
+ SetMockMobileOperatorInfoObjects();
+
+ mm1::MockModemModemCdmaProxy *cdma_proxy = modem_cdma_proxy_.get();
+ SetUp();
+ capability_->InitProxies();
+
+ // Cases when activation fails because |activation_code| is not available.
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*cdma_proxy, Activate(_, _, _, _)).Times(0);
+ capability_->ActivateAutomatic();
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ Mock::VerifyAndClearExpectations(modem_cdma_proxy_.get());
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*cdma_proxy, Activate(_, _, _, _)).Times(0);
+ capability_->ActivateAutomatic();
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ Mock::VerifyAndClearExpectations(modem_cdma_proxy_.get());
+
+ // These expectations hold for all subsequent tests.
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, activation_code())
+ .WillRepeatedly(ReturnRef(activation_code));
+
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierMEID, _))
+ .WillOnce(Return(PendingActivationStore::kStatePending))
+ .WillOnce(Return(PendingActivationStore::kStateActivated));
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ SetActivationState(_, _, _))
+ .Times(0);
+ EXPECT_CALL(*cdma_proxy, Activate(_, _, _, _)).Times(0);
+ capability_->ActivateAutomatic();
+ capability_->ActivateAutomatic();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(modem_cdma_proxy_.get());
+
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierMEID, _))
+ .WillOnce(Return(PendingActivationStore::kStateUnknown))
+ .WillOnce(Return(PendingActivationStore::kStateFailureRetry));
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ SetActivationState(_, _, PendingActivationStore::kStatePending))
+ .Times(2);
+ EXPECT_CALL(*cdma_proxy, Activate(_, _, _, _)).Times(2);
+ capability_->ActivateAutomatic();
+ capability_->ActivateAutomatic();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(modem_cdma_proxy_.get());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, IsServiceActivationRequired) {
+ const vector<MobileOperatorInfo::OnlinePortal> empty_list;
+ const vector<MobileOperatorInfo::OnlinePortal> olp_list {
+ {"some@url", "some_method", "some_post_data"}
+ };
+ SetMockMobileOperatorInfoObjects();
+
+ capability_->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+
+ capability_->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, olp_list())
+ .WillRepeatedly(ReturnRef(empty_list));
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+
+ // These expectations hold for all subsequent tests.
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, olp_list())
+ .WillRepeatedly(ReturnRef(olp_list));
+
+ capability_->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+ EXPECT_TRUE(capability_->IsServiceActivationRequired());
+ capability_->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+ capability_->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest,
+ UpdateServiceActivationStateProperty) {
+ const vector<MobileOperatorInfo::OnlinePortal> olp_list {
+ {"some@url", "some_method", "some_post_data"}
+ };
+ SetMockMobileOperatorInfoObjects();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, olp_list())
+ .WillRepeatedly(ReturnRef(olp_list));
+
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _))
+ .WillOnce(Return(PendingActivationStore::kStatePending))
+ .WillRepeatedly(Return(PendingActivationStore::kStateUnknown));
+
+ capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivating))
+ .Times(1);
+ capability_->UpdateServiceActivationStateProperty();
+ Mock::VerifyAndClearExpectations(service_);
+
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateNotActivated))
+ .Times(1);
+ capability_->UpdateServiceActivationStateProperty();
+ Mock::VerifyAndClearExpectations(service_);
+
+ capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivating))
+ .Times(1);
+ capability_->UpdateServiceActivationStateProperty();
+ Mock::VerifyAndClearExpectations(service_);
+
+ capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivated))
+ .Times(1);
+ capability_->UpdateServiceActivationStateProperty();
+ Mock::VerifyAndClearExpectations(service_);
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, IsActivating) {
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _))
+ .WillOnce(Return(PendingActivationStore::kStatePending))
+ .WillOnce(Return(PendingActivationStore::kStatePending))
+ .WillOnce(Return(PendingActivationStore::kStateFailureRetry))
+ .WillRepeatedly(Return(PendingActivationStore::kStateUnknown));
+
+ capability_->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+ EXPECT_TRUE(capability_->IsActivating());
+ EXPECT_TRUE(capability_->IsActivating());
+ capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+ EXPECT_TRUE(capability_->IsActivating());
+ EXPECT_TRUE(capability_->IsActivating());
+ capability_->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+ EXPECT_FALSE(capability_->IsActivating());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, IsRegistered) {
+ capability_->cdma_1x_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ EXPECT_FALSE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_1x_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_1x_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_1x_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->cdma_evdo_registration_state_ =
+ MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
+ EXPECT_TRUE(capability_->IsRegistered());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, SetupConnectProperties) {
+ DBusPropertiesMap map;
+ capability_->SetupConnectProperties(&map);
+ EXPECT_EQ(1, map.size());
+ EXPECT_STREQ("#777", map["number"].reader().get_string());
+}
+
+TEST_F(CellularCapabilityUniversalCDMADispatcherTest,
+ UpdatePendingActivationState) {
+ capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_, _))
+ .Times(1);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _))
+ .Times(0);
+ EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(0);
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+
+ capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_, _))
+ .Times(0);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _))
+ .Times(2)
+ .WillRepeatedly(Return(PendingActivationStore::kStateUnknown));
+ EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(0);
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+
+ capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_, _))
+ .Times(0);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _))
+ .Times(2)
+ .WillRepeatedly(Return(PendingActivationStore::kStatePending));
+ EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(0);
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_, _))
+ .Times(0);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _))
+ .Times(2)
+ .WillRepeatedly(Return(PendingActivationStore::kStateFailureRetry));
+ EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(1);
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_, _))
+ .Times(0);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _))
+ .Times(4)
+ .WillOnce(Return(PendingActivationStore::kStateActivated))
+ .WillOnce(Return(PendingActivationStore::kStateActivated))
+ .WillOnce(Return(PendingActivationStore::kStateUnknown))
+ .WillOnce(Return(PendingActivationStore::kStateUnknown));
+ EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(0);
+ capability_->UpdatePendingActivationState();
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+}
+
+} // namespace shill
diff --git a/cellular/cellular_capability_universal_unittest.cc b/cellular/cellular_capability_universal_unittest.cc
new file mode 100644
index 0000000..4566799
--- /dev/null
+++ b/cellular/cellular_capability_universal_unittest.cc
@@ -0,0 +1,2025 @@
+// Copyright (c) 2013 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/cellular_capability_universal.h"
+
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <ModemManager/ModemManager.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_bearer.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/mock_cellular.h"
+#include "shill/cellular/mock_cellular_service.h"
+#include "shill/cellular/mock_mm1_modem_modem3gpp_proxy.h"
+#include "shill/cellular/mock_mm1_modem_modemcdma_proxy.h"
+#include "shill/cellular/mock_mm1_modem_proxy.h"
+#include "shill/cellular/mock_mm1_modem_simple_proxy.h"
+#include "shill/cellular/mock_mm1_sim_proxy.h"
+#include "shill/cellular/mock_mobile_operator_info.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/dbus_adaptor.h"
+#include "shill/error.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_adaptors.h"
+#include "shill/mock_dbus_properties_proxy.h"
+#include "shill/mock_event_dispatcher.h"
+#include "shill/mock_pending_activation_store.h"
+#include "shill/mock_profile.h"
+#include "shill/net/mock_rtnl_handler.h"
+#include "shill/proxy_factory.h"
+#include "shill/testing.h"
+
+using base::Bind;
+using base::StringPrintf;
+using base::Unretained;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using testing::AnyNumber;
+using testing::InSequence;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SaveArg;
+using testing::_;
+
+namespace shill {
+
+MATCHER_P(HasApn, expected_apn, "") {
+ string apn;
+ return (DBusProperties::GetString(arg,
+ CellularCapabilityUniversal::kConnectApn,
+ &apn) &&
+ apn == expected_apn);
+}
+
+class CellularCapabilityUniversalTest : public testing::TestWithParam<string> {
+ public:
+ explicit CellularCapabilityUniversalTest(EventDispatcher *dispatcher)
+ : dispatcher_(dispatcher),
+ modem_info_(nullptr, dispatcher, nullptr, nullptr, nullptr),
+ modem_3gpp_proxy_(new mm1::MockModemModem3gppProxy()),
+ modem_cdma_proxy_(new mm1::MockModemModemCdmaProxy()),
+ modem_proxy_(new mm1::MockModemProxy()),
+ modem_simple_proxy_(new mm1::MockModemSimpleProxy()),
+ sim_proxy_(new mm1::MockSimProxy()),
+ properties_proxy_(new MockDBusPropertiesProxy()),
+ proxy_factory_(this),
+ capability_(nullptr),
+ device_adaptor_(nullptr),
+ cellular_(new Cellular(&modem_info_,
+ "",
+ "00:01:02:03:04:05",
+ 0,
+ Cellular::kTypeUniversal,
+ "",
+ "",
+ "",
+ &proxy_factory_)),
+ service_(new MockCellularService(&modem_info_, cellular_)),
+ mock_home_provider_info_(nullptr),
+ mock_serving_operator_info_(nullptr) {
+ modem_info_.metrics()->RegisterDevice(cellular_->interface_index(),
+ Technology::kCellular);
+ }
+
+ virtual ~CellularCapabilityUniversalTest() {
+ cellular_->service_ = nullptr;
+ capability_ = nullptr;
+ device_adaptor_ = nullptr;
+ }
+
+ virtual void SetUp() {
+ capability_ = dynamic_cast<CellularCapabilityUniversal *>(
+ cellular_->capability_.get());
+ device_adaptor_ =
+ dynamic_cast<DeviceMockAdaptor *>(cellular_->adaptor());
+ cellular_->service_ = service_;
+
+ // kStateUnknown leads to minimal extra work in maintaining
+ // activation state.
+ ON_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .WillByDefault(Return(PendingActivationStore::kStateUnknown));
+
+ SetMockMobileOperatorInfoObjects();
+ }
+
+ virtual void TearDown() {
+ capability_->proxy_factory_ = nullptr;
+ }
+
+ void CreateService() {
+ // The following constants are never directly accessed by the tests.
+ const char kStorageIdentifier[] = "default_test_storage_id";
+ const char kFriendlyServiceName[] = "default_test_service_name";
+ const char kOperatorCode[] = "10010";
+ const char kOperatorName[] = "default_test_operator_name";
+ const char kOperatorCountry[] = "us";
+
+ // Simulate all the side-effects of Cellular::CreateService
+ auto service = new CellularService(&modem_info_, cellular_);
+ service->SetStorageIdentifier(kStorageIdentifier);
+ service->SetFriendlyName(kFriendlyServiceName);
+
+ Stringmap serving_operator;
+ serving_operator[kOperatorCodeKey] = kOperatorCode;
+ serving_operator[kOperatorNameKey] = kOperatorName;
+ serving_operator[kOperatorCountryKey] = kOperatorCountry;
+ service->set_serving_operator(serving_operator);
+ cellular_->set_home_provider(serving_operator);
+ cellular_->service_ = service;
+ }
+
+ void ClearService() {
+ cellular_->service_ = nullptr;
+ }
+
+ void ExpectModemAndModem3gppProperties() {
+ // Set up mock modem properties.
+ DBusPropertiesMap modem_properties;
+ string operator_name = "TestOperator";
+ string operator_code = "001400";
+
+ modem_properties[MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES].
+ writer().append_uint32(kAccessTechnologies);
+
+ DBus::Variant variant;
+ DBus::MessageIter writer = variant.writer();
+ DBus::Struct<uint32_t, bool> signal_signal;
+ signal_signal._1 = 90;
+ signal_signal._2 = true;
+ writer << signal_signal;
+ modem_properties[MM_MODEM_PROPERTY_SIGNALQUALITY] = variant;
+
+ // Set up mock modem 3gpp properties.
+ DBusPropertiesMap modem3gpp_properties;
+ modem3gpp_properties[MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS].
+ writer().append_uint32(0);
+ modem3gpp_properties[MM_MODEM_MODEM3GPP_PROPERTY_IMEI].
+ writer().append_string(kImei);
+
+ EXPECT_CALL(*properties_proxy_,
+ GetAll(MM_DBUS_INTERFACE_MODEM))
+ .WillOnce(Return(modem_properties));
+ EXPECT_CALL(*properties_proxy_,
+ GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP))
+ .WillOnce(Return(modem3gpp_properties));
+ }
+
+ void InvokeEnable(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeEnableFail(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error(Error::kOperationFailed));
+ }
+ void InvokeEnableInWrongState(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error(Error::kWrongState));
+ }
+ void InvokeRegister(const string &operator_id, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeSetPowerState(const uint32_t &power_state,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ callback.Run(Error());
+ }
+ void Set3gppProxy() {
+ capability_->modem_3gpp_proxy_.reset(modem_3gpp_proxy_.release());
+ }
+
+ void SetSimpleProxy() {
+ capability_->modem_simple_proxy_.reset(modem_simple_proxy_.release());
+ }
+
+ void SetMockMobileOperatorInfoObjects() {
+ CHECK(!mock_home_provider_info_);
+ CHECK(!mock_serving_operator_info_);
+ mock_home_provider_info_ =
+ new MockMobileOperatorInfo(dispatcher_, "HomeProvider");
+ mock_serving_operator_info_ =
+ new MockMobileOperatorInfo(dispatcher_, "ServingOperator");
+ cellular_->set_home_provider_info(mock_home_provider_info_);
+ cellular_->set_serving_operator_info(mock_serving_operator_info_);
+ }
+
+ void ReleaseCapabilityProxies() {
+ capability_->ReleaseProxies();
+ }
+
+ void SetRegistrationDroppedUpdateTimeout(int64_t timeout_milliseconds) {
+ capability_->registration_dropped_update_timeout_milliseconds_ =
+ timeout_milliseconds;
+ }
+
+ MOCK_METHOD1(TestCallback, void(const Error &error));
+
+ MOCK_METHOD0(DummyCallback, void(void));
+
+ void SetMockRegistrationDroppedUpdateCallback() {
+ capability_->registration_dropped_update_callback_.Reset(
+ Bind(&CellularCapabilityUniversalTest::DummyCallback,
+ Unretained(this)));
+ }
+
+ protected:
+ static const char kActiveBearerPathPrefix[];
+ static const char kImei[];
+ static const char kInactiveBearerPathPrefix[];
+ static const char kSimPath[];
+ static const uint32_t kAccessTechnologies;
+ static const char kTestMobileProviderDBPath[];
+
+ class TestProxyFactory : public ProxyFactory {
+ public:
+ explicit TestProxyFactory(CellularCapabilityUniversalTest *test) :
+ test_(test) {
+ active_bearer_properties_[MM_BEARER_PROPERTY_CONNECTED].writer()
+ .append_bool(true);
+ active_bearer_properties_[MM_BEARER_PROPERTY_INTERFACE].writer()
+ .append_string("/dev/fake");
+
+ DBusPropertiesMap ip4config;
+ ip4config["method"].writer().append_uint32(MM_BEARER_IP_METHOD_DHCP);
+ DBus::MessageIter writer =
+ active_bearer_properties_[MM_BEARER_PROPERTY_IP4CONFIG].writer();
+ writer << ip4config;
+
+ inactive_bearer_properties_[MM_BEARER_PROPERTY_CONNECTED].writer()
+ .append_bool(false);
+ }
+
+ DBusPropertiesMap *mutable_active_bearer_properties() {
+ return &active_bearer_properties_;
+ }
+
+ DBusPropertiesMap *mutable_inactive_bearer_properties() {
+ return &inactive_bearer_properties_;
+ }
+
+ virtual mm1::ModemModem3gppProxyInterface *CreateMM1ModemModem3gppProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->modem_3gpp_proxy_.release();
+ }
+
+ virtual mm1::ModemModemCdmaProxyInterface *CreateMM1ModemModemCdmaProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->modem_cdma_proxy_.release();
+ }
+
+ virtual mm1::ModemProxyInterface *CreateMM1ModemProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->modem_proxy_.release();
+ }
+
+ virtual mm1::ModemSimpleProxyInterface *CreateMM1ModemSimpleProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ return test_->modem_simple_proxy_.release();
+ }
+
+ virtual mm1::SimProxyInterface *CreateSimProxy(
+ const std::string &/*path*/,
+ const std::string &/*service*/) {
+ mm1::MockSimProxy *sim_proxy = test_->sim_proxy_.release();
+ test_->sim_proxy_.reset(new mm1::MockSimProxy());
+ return sim_proxy;
+ }
+
+ virtual DBusPropertiesProxyInterface *CreateDBusPropertiesProxy(
+ const std::string &path,
+ const std::string &/*service*/) {
+ MockDBusPropertiesProxy *properties_proxy =
+ test_->properties_proxy_.release();
+ if (path.find(kActiveBearerPathPrefix) != std::string::npos) {
+ EXPECT_CALL(*properties_proxy, GetAll(MM_DBUS_INTERFACE_BEARER))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(active_bearer_properties_));
+ } else {
+ EXPECT_CALL(*properties_proxy, GetAll(MM_DBUS_INTERFACE_BEARER))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(inactive_bearer_properties_));
+ }
+ test_->properties_proxy_.reset(new MockDBusPropertiesProxy());
+ return properties_proxy;
+ }
+
+ private:
+ CellularCapabilityUniversalTest *test_;
+ DBusPropertiesMap active_bearer_properties_;
+ DBusPropertiesMap inactive_bearer_properties_;
+ };
+
+ EventDispatcher *dispatcher_;
+ MockModemInfo modem_info_;
+ unique_ptr<mm1::MockModemModem3gppProxy> modem_3gpp_proxy_;
+ unique_ptr<mm1::MockModemModemCdmaProxy> modem_cdma_proxy_;
+ unique_ptr<mm1::MockModemProxy> modem_proxy_;
+ unique_ptr<mm1::MockModemSimpleProxy> modem_simple_proxy_;
+ unique_ptr<mm1::MockSimProxy> sim_proxy_;
+ unique_ptr<MockDBusPropertiesProxy> properties_proxy_;
+ TestProxyFactory proxy_factory_;
+ CellularCapabilityUniversal *capability_; // Owned by |cellular_|.
+ DeviceMockAdaptor *device_adaptor_; // Owned by |cellular_|.
+ CellularRefPtr cellular_;
+ MockCellularService *service_; // owned by cellular_
+ DBusPathCallback connect_callback_; // saved for testing connect operations
+
+ // Set when required and passed to |cellular_|. Owned by |cellular_|.
+ MockMobileOperatorInfo *mock_home_provider_info_;
+ MockMobileOperatorInfo *mock_serving_operator_info_;
+};
+
+// Most of our tests involve using a real EventDispatcher object.
+class CellularCapabilityUniversalMainTest
+ : public CellularCapabilityUniversalTest {
+ public:
+ CellularCapabilityUniversalMainTest() :
+ CellularCapabilityUniversalTest(&dispatcher_) {}
+
+ protected:
+ EventDispatcher dispatcher_;
+};
+
+// Tests that involve timers will (or may) use a mock of the event dispatcher
+// instead of a real one.
+class CellularCapabilityUniversalTimerTest
+ : public CellularCapabilityUniversalTest {
+ public:
+ CellularCapabilityUniversalTimerTest()
+ : CellularCapabilityUniversalTest(&mock_dispatcher_) {}
+
+ protected:
+ ::testing::StrictMock<MockEventDispatcher> mock_dispatcher_;
+};
+
+const char CellularCapabilityUniversalTest::kActiveBearerPathPrefix[] =
+ "/bearer/active";
+const char CellularCapabilityUniversalTest::kImei[] = "999911110000";
+const char CellularCapabilityUniversalTest::kInactiveBearerPathPrefix[] =
+ "/bearer/inactive";
+const char CellularCapabilityUniversalTest::kSimPath[] = "/foo/sim";
+const uint32_t CellularCapabilityUniversalTest::kAccessTechnologies =
+ MM_MODEM_ACCESS_TECHNOLOGY_LTE |
+ MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS;
+const char CellularCapabilityUniversalTest::kTestMobileProviderDBPath[] =
+ "provider_db_unittest.bfd";
+
+TEST_F(CellularCapabilityUniversalMainTest, StartModem) {
+ ExpectModemAndModem3gppProperties();
+
+ EXPECT_CALL(*modem_proxy_,
+ Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(
+ this, &CellularCapabilityUniversalTest::InvokeEnable));
+
+ Error error;
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ capability_->StartModem(&error, callback);
+
+ EXPECT_TRUE(error.IsOngoing());
+ EXPECT_EQ(kImei, cellular_->imei());
+ EXPECT_EQ(kAccessTechnologies, capability_->access_technologies_);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, StartModemFailure) {
+ EXPECT_CALL(*modem_proxy_,
+ Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(
+ this, &CellularCapabilityUniversalTest::InvokeEnableFail));
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_MODEM)).Times(0);
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP))
+ .Times(0);
+
+ Error error;
+ EXPECT_CALL(*this, TestCallback(IsFailure()));
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ capability_->StartModem(&error, callback);
+ EXPECT_TRUE(error.IsOngoing());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, StartModemInWrongState) {
+ ExpectModemAndModem3gppProperties();
+
+ EXPECT_CALL(*modem_proxy_,
+ Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(
+ this, &CellularCapabilityUniversalTest::InvokeEnableInWrongState))
+ .WillOnce(Invoke(
+ this, &CellularCapabilityUniversalTest::InvokeEnable));
+
+ Error error;
+ EXPECT_CALL(*this, TestCallback(_)).Times(0);
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ capability_->StartModem(&error, callback);
+ EXPECT_TRUE(error.IsOngoing());
+
+ // Verify that the modem has not been enabled.
+ EXPECT_TRUE(cellular_->imei().empty());
+ EXPECT_EQ(0, capability_->access_technologies_);
+ Mock::VerifyAndClearExpectations(this);
+
+ // Change the state to kModemStateEnabling and verify that it still has not
+ // been enabled.
+ capability_->OnModemStateChanged(Cellular::kModemStateEnabling);
+ EXPECT_TRUE(cellular_->imei().empty());
+ EXPECT_EQ(0, capability_->access_technologies_);
+ Mock::VerifyAndClearExpectations(this);
+
+ // Change the state to kModemStateDisabling and verify that it still has not
+ // been enabled.
+ EXPECT_CALL(*this, TestCallback(_)).Times(0);
+ capability_->OnModemStateChanged(Cellular::kModemStateDisabling);
+ EXPECT_TRUE(cellular_->imei().empty());
+ EXPECT_EQ(0, capability_->access_technologies_);
+ Mock::VerifyAndClearExpectations(this);
+
+ // Change the state of the modem to disabled and verify that it gets enabled.
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ capability_->OnModemStateChanged(Cellular::kModemStateDisabled);
+ EXPECT_EQ(kImei, cellular_->imei());
+ EXPECT_EQ(kAccessTechnologies, capability_->access_technologies_);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest,
+ StartModemWithDeferredEnableFailure) {
+ EXPECT_CALL(*modem_proxy_,
+ Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .Times(2)
+ .WillRepeatedly(Invoke(
+ this, &CellularCapabilityUniversalTest::InvokeEnableInWrongState));
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_MODEM)).Times(0);
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP))
+ .Times(0);
+
+ Error error;
+ EXPECT_CALL(*this, TestCallback(_)).Times(0);
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ capability_->StartModem(&error, callback);
+ EXPECT_TRUE(error.IsOngoing());
+ Mock::VerifyAndClearExpectations(this);
+
+ // Change the state of the modem to disabled but fail the deferred enable
+ // operation with the WrongState error in order to verify that the deferred
+ // enable operation does not trigger another deferred enable operation.
+ EXPECT_CALL(*this, TestCallback(IsFailure()));
+ capability_->OnModemStateChanged(Cellular::kModemStateDisabled);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, StopModem) {
+ // Save pointers to proxies before they are lost by the call to InitProxies
+ mm1::MockModemProxy *modem_proxy = modem_proxy_.get();
+ EXPECT_CALL(*modem_proxy, set_state_changed_callback(_));
+ capability_->InitProxies();
+
+ Error error;
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ capability_->StopModem(&error, callback);
+ EXPECT_TRUE(error.IsSuccess());
+
+ ResultCallback disable_callback;
+ EXPECT_CALL(*modem_proxy,
+ Enable(false, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(SaveArg<2>(&disable_callback));
+ dispatcher_.DispatchPendingEvents();
+
+ ResultCallback set_power_state_callback;
+ EXPECT_CALL(
+ *modem_proxy,
+ SetPowerState(
+ MM_MODEM_POWER_STATE_LOW, _, _,
+ CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds))
+ .WillOnce(SaveArg<2>(&set_power_state_callback));
+ disable_callback.Run(Error(Error::kSuccess));
+
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ set_power_state_callback.Run(Error(Error::kSuccess));
+ Mock::VerifyAndClearExpectations(this);
+
+ // TestCallback should get called with success even if the power state
+ // callback gets called with an error
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ set_power_state_callback.Run(Error(Error::kOperationFailed));
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, StopModemAltair) {
+ // Save pointers to proxies before they are lost by the call to InitProxies
+ mm1::MockModemProxy *modem_proxy = modem_proxy_.get();
+ EXPECT_CALL(*modem_proxy, set_state_changed_callback(_));
+ capability_->InitProxies();
+
+ const char kBearerDBusPath[] = "/bearer/dbus/path";
+ capability_->set_active_bearer(
+ new CellularBearer(&proxy_factory_,
+ kBearerDBusPath,
+ cellular_->dbus_service())); // Passes ownership.
+
+ cellular_->set_mm_plugin(CellularCapabilityUniversal::kAltairLTEMMPlugin);
+
+ Error error;
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ capability_->StopModem(&error, callback);
+ EXPECT_TRUE(error.IsSuccess());
+
+ ResultCallback delete_bearer_callback;
+ EXPECT_CALL(*modem_proxy,
+ DeleteBearer(::DBus::Path(kBearerDBusPath), _, _,
+ CellularCapability::kTimeoutDefault))
+ .WillOnce(SaveArg<2>(&delete_bearer_callback));
+ dispatcher_.DispatchPendingEvents();
+
+ ResultCallback disable_callback;
+ EXPECT_CALL(*modem_proxy,
+ Enable(false, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(SaveArg<2>(&disable_callback));
+ delete_bearer_callback.Run(Error(Error::kSuccess));
+
+ ResultCallback set_power_state_callback;
+ EXPECT_CALL(
+ *modem_proxy,
+ SetPowerState(
+ MM_MODEM_POWER_STATE_LOW, _, _,
+ CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds))
+ .WillOnce(SaveArg<2>(&set_power_state_callback));
+ disable_callback.Run(Error(Error::kSuccess));
+
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ set_power_state_callback.Run(Error(Error::kSuccess));
+}
+
+TEST_F(CellularCapabilityUniversalMainTest,
+ StopModemAltairDeleteBearerFailure) {
+ // Save pointers to proxies before they are lost by the call to InitProxies
+ mm1::MockModemProxy *modem_proxy = modem_proxy_.get();
+ EXPECT_CALL(*modem_proxy, set_state_changed_callback(_));
+ capability_->InitProxies();
+
+ const char kBearerDBusPath[] = "/bearer/dbus/path";
+ capability_->set_active_bearer(
+ new CellularBearer(&proxy_factory_,
+ kBearerDBusPath,
+ cellular_->dbus_service())); // Passes ownership.
+
+ cellular_->set_mm_plugin(CellularCapabilityUniversal::kAltairLTEMMPlugin);
+
+ Error error;
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ capability_->StopModem(&error, callback);
+ EXPECT_TRUE(error.IsSuccess());
+
+ ResultCallback delete_bearer_callback;
+ EXPECT_CALL(*modem_proxy,
+ DeleteBearer(::DBus::Path(kBearerDBusPath), _, _,
+ CellularCapability::kTimeoutDefault))
+ .WillOnce(SaveArg<2>(&delete_bearer_callback));
+ dispatcher_.DispatchPendingEvents();
+
+ ResultCallback disable_callback;
+ EXPECT_CALL(*modem_proxy,
+ Enable(false, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(SaveArg<2>(&disable_callback));
+ delete_bearer_callback.Run(Error(Error::kOperationFailed));
+
+ ResultCallback set_power_state_callback;
+ EXPECT_CALL(
+ *modem_proxy,
+ SetPowerState(
+ MM_MODEM_POWER_STATE_LOW, _, _,
+ CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds))
+ .WillOnce(SaveArg<2>(&set_power_state_callback));
+ disable_callback.Run(Error(Error::kSuccess));
+
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ set_power_state_callback.Run(Error(Error::kSuccess));
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, StopModemAltairNotConnected) {
+ // Save pointers to proxies before they are lost by the call to InitProxies
+ mm1::MockModemProxy *modem_proxy = modem_proxy_.get();
+ EXPECT_CALL(*modem_proxy, set_state_changed_callback(_));
+ capability_->InitProxies();
+ capability_->set_active_bearer(nullptr);
+ cellular_->set_mm_plugin(CellularCapabilityUniversal::kAltairLTEMMPlugin);
+
+ Error error;
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ capability_->StopModem(&error, callback);
+ EXPECT_TRUE(error.IsSuccess());
+
+ ResultCallback disable_callback;
+ EXPECT_CALL(*modem_proxy,
+ Enable(false, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(SaveArg<2>(&disable_callback));
+ dispatcher_.DispatchPendingEvents();
+
+ ResultCallback set_power_state_callback;
+ EXPECT_CALL(
+ *modem_proxy,
+ SetPowerState(
+ MM_MODEM_POWER_STATE_LOW, _, _,
+ CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds))
+ .WillOnce(SaveArg<2>(&set_power_state_callback));
+ disable_callback.Run(Error(Error::kSuccess));
+
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ set_power_state_callback.Run(Error(Error::kSuccess));
+ Mock::VerifyAndClearExpectations(this);
+
+ // TestCallback should get called with success even if the power state
+ // callback gets called with an error
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ set_power_state_callback.Run(Error(Error::kOperationFailed));
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, TerminationAction) {
+ ExpectModemAndModem3gppProperties();
+
+ {
+ InSequence seq;
+
+ EXPECT_CALL(*modem_proxy_,
+ Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularCapabilityUniversalTest::InvokeEnable));
+ EXPECT_CALL(*modem_proxy_,
+ Enable(false, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularCapabilityUniversalTest::InvokeEnable));
+ EXPECT_CALL(
+ *modem_proxy_,
+ SetPowerState(
+ MM_MODEM_POWER_STATE_LOW, _, _,
+ CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds))
+ .WillOnce(Invoke(
+ this, &CellularCapabilityUniversalTest::InvokeSetPowerState));
+ }
+ EXPECT_CALL(*this, TestCallback(IsSuccess())).Times(2);
+
+ EXPECT_EQ(Cellular::kStateDisabled, cellular_->state());
+ EXPECT_EQ(Cellular::kModemStateUnknown, cellular_->modem_state());
+ EXPECT_TRUE(modem_info_.manager()->termination_actions_.IsEmpty());
+
+ // Here we mimic the modem state change from ModemManager. When the modem is
+ // enabled, a termination action should be added.
+ cellular_->OnModemStateChanged(Cellular::kModemStateEnabled);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateEnabled, cellular_->state());
+ EXPECT_EQ(Cellular::kModemStateEnabled, cellular_->modem_state());
+ EXPECT_FALSE(modem_info_.manager()->termination_actions_.IsEmpty());
+
+ // Running the termination action should disable the modem.
+ modem_info_.manager()->RunTerminationActions(Bind(
+ &CellularCapabilityUniversalMainTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+ // Here we mimic the modem state change from ModemManager. When the modem is
+ // disabled, the termination action should be removed.
+ cellular_->OnModemStateChanged(Cellular::kModemStateDisabled);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateDisabled, cellular_->state());
+ EXPECT_EQ(Cellular::kModemStateDisabled, cellular_->modem_state());
+ EXPECT_TRUE(modem_info_.manager()->termination_actions_.IsEmpty());
+
+ // No termination action should be called here.
+ modem_info_.manager()->RunTerminationActions(Bind(
+ &CellularCapabilityUniversalMainTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularCapabilityUniversalMainTest,
+ TerminationActionRemovedByStopModem) {
+ ExpectModemAndModem3gppProperties();
+
+ {
+ InSequence seq;
+
+ EXPECT_CALL(*modem_proxy_,
+ Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularCapabilityUniversalTest::InvokeEnable));
+ EXPECT_CALL(*modem_proxy_,
+ Enable(false, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularCapabilityUniversalTest::InvokeEnable));
+ EXPECT_CALL(
+ *modem_proxy_,
+ SetPowerState(
+ MM_MODEM_POWER_STATE_LOW, _, _,
+ CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds))
+ .WillOnce(Invoke(
+ this, &CellularCapabilityUniversalTest::InvokeSetPowerState));
+ }
+ EXPECT_CALL(*this, TestCallback(IsSuccess())).Times(1);
+
+ EXPECT_EQ(Cellular::kStateDisabled, cellular_->state());
+ EXPECT_EQ(Cellular::kModemStateUnknown, cellular_->modem_state());
+ EXPECT_TRUE(modem_info_.manager()->termination_actions_.IsEmpty());
+
+ // Here we mimic the modem state change from ModemManager. When the modem is
+ // enabled, a termination action should be added.
+ cellular_->OnModemStateChanged(Cellular::kModemStateEnabled);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateEnabled, cellular_->state());
+ EXPECT_EQ(Cellular::kModemStateEnabled, cellular_->modem_state());
+ EXPECT_FALSE(modem_info_.manager()->termination_actions_.IsEmpty());
+
+ // Verify that the termination action is removed when the modem is disabled
+ // not due to a suspend request.
+ cellular_->SetEnabled(false);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateDisabled, cellular_->state());
+ EXPECT_TRUE(modem_info_.manager()->termination_actions_.IsEmpty());
+
+ // No termination action should be called here.
+ modem_info_.manager()->RunTerminationActions(Bind(
+ &CellularCapabilityUniversalMainTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, DisconnectModemNoBearer) {
+ Error error;
+ ResultCallback disconnect_callback;
+ EXPECT_CALL(*modem_simple_proxy_,
+ Disconnect(_, _, _, CellularCapability::kTimeoutDisconnect))
+ .Times(0);
+ capability_->Disconnect(&error, disconnect_callback);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, DisconnectNoProxy) {
+ Error error;
+ ResultCallback disconnect_callback;
+ EXPECT_CALL(*modem_simple_proxy_,
+ Disconnect(_, _, _, CellularCapability::kTimeoutDisconnect))
+ .Times(0);
+ ReleaseCapabilityProxies();
+ capability_->Disconnect(&error, disconnect_callback);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, SimLockStatusChanged) {
+ // Set up mock SIM properties
+ const char kImsi[] = "310100000001";
+ const char kSimIdentifier[] = "9999888";
+ const char kOperatorIdentifier[] = "310240";
+ const char kOperatorName[] = "Custom SPN";
+ DBusPropertiesMap sim_properties;
+ sim_properties[MM_SIM_PROPERTY_IMSI].writer().append_string(kImsi);
+ sim_properties[MM_SIM_PROPERTY_SIMIDENTIFIER].writer()
+ .append_string(kSimIdentifier);
+ sim_properties[MM_SIM_PROPERTY_OPERATORIDENTIFIER].writer()
+ .append_string(kOperatorIdentifier);
+ sim_properties[MM_SIM_PROPERTY_OPERATORNAME].writer()
+ .append_string(kOperatorName);
+
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_SIM))
+ .WillOnce(Return(sim_properties));
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .Times(1);
+
+ EXPECT_FALSE(cellular_->sim_present());
+ EXPECT_EQ(nullptr, capability_->sim_proxy_);;
+
+ capability_->OnSimPathChanged(kSimPath);
+ EXPECT_TRUE(cellular_->sim_present());
+ EXPECT_NE(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ(kSimPath, capability_->sim_path_);
+
+ cellular_->set_imsi("");
+ cellular_->set_sim_identifier("");
+ capability_->spn_ = "";
+
+ // SIM is locked.
+ capability_->sim_lock_status_.lock_type = MM_MODEM_LOCK_SIM_PIN;
+ capability_->OnSimLockStatusChanged();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ EXPECT_EQ("", cellular_->imsi());
+ EXPECT_EQ("", cellular_->sim_identifier());
+ EXPECT_EQ("", capability_->spn_);
+
+ // SIM is unlocked.
+ properties_proxy_.reset(new MockDBusPropertiesProxy());
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_SIM))
+ .WillOnce(Return(sim_properties));
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .Times(1);
+
+ capability_->sim_lock_status_.lock_type = MM_MODEM_LOCK_NONE;
+ capability_->OnSimLockStatusChanged();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ EXPECT_EQ(kImsi, cellular_->imsi());
+ EXPECT_EQ(kSimIdentifier, cellular_->sim_identifier());
+ EXPECT_EQ(kOperatorName, capability_->spn_);
+
+ // SIM is missing and SIM path is "/".
+ capability_->OnSimPathChanged(CellularCapabilityUniversal::kRootPath);
+ EXPECT_FALSE(cellular_->sim_present());
+ EXPECT_EQ(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ(CellularCapabilityUniversal::kRootPath, capability_->sim_path_);
+
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _)).Times(0);
+ capability_->OnSimLockStatusChanged();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ EXPECT_EQ("", cellular_->imsi());
+ EXPECT_EQ("", cellular_->sim_identifier());
+ EXPECT_EQ("", capability_->spn_);
+
+ // SIM is missing and SIM path is empty.
+ capability_->OnSimPathChanged("");
+ EXPECT_FALSE(cellular_->sim_present());
+ EXPECT_EQ(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ("", capability_->sim_path_);
+
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(_, _)).Times(0);
+ capability_->OnSimLockStatusChanged();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ EXPECT_EQ("", cellular_->imsi());
+ EXPECT_EQ("", cellular_->sim_identifier());
+ EXPECT_EQ("", capability_->spn_);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, PropertiesChanged) {
+ // Set up mock modem properties
+ DBusPropertiesMap modem_properties;
+ modem_properties[MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES].
+ writer().append_uint32(kAccessTechnologies);
+ modem_properties[MM_MODEM_PROPERTY_SIM].
+ writer().append_path(kSimPath);
+
+ // Set up mock modem 3gpp properties
+ DBusPropertiesMap modem3gpp_properties;
+ modem3gpp_properties[MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS].
+ writer().append_uint32(0);
+ modem3gpp_properties[MM_MODEM_MODEM3GPP_PROPERTY_IMEI].
+ writer().append_string(kImei);
+
+ // Set up mock modem sim properties
+ DBusPropertiesMap sim_properties;
+
+ EXPECT_CALL(*properties_proxy_,
+ GetAll(MM_DBUS_INTERFACE_SIM))
+ .WillOnce(Return(sim_properties));
+
+ EXPECT_EQ("", cellular_->imei());
+ EXPECT_EQ(MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN,
+ capability_->access_technologies_);
+ EXPECT_FALSE(capability_->sim_proxy_.get());
+ EXPECT_CALL(*device_adaptor_, EmitStringChanged(
+ kTechnologyFamilyProperty, kTechnologyFamilyGsm));
+ EXPECT_CALL(*device_adaptor_, EmitStringChanged(kImeiProperty, kImei));
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM,
+ modem_properties, vector<string>());
+ EXPECT_EQ(kAccessTechnologies, capability_->access_technologies_);
+ EXPECT_EQ(kSimPath, capability_->sim_path_);
+ EXPECT_TRUE(capability_->sim_proxy_.get());
+
+ // Changing properties on wrong interface will not have an effect
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM,
+ modem3gpp_properties,
+ vector<string>());
+ EXPECT_EQ("", cellular_->imei());
+
+ // Changing properties on the right interface gets reflected in the
+ // capabilities object
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM_MODEM3GPP,
+ modem3gpp_properties,
+ vector<string>());
+ EXPECT_EQ(kImei, cellular_->imei());
+ Mock::VerifyAndClearExpectations(device_adaptor_);
+
+ // Expect to see changes when the family changes
+ modem_properties.clear();
+ modem_properties[MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES].
+ writer().append_uint32(MM_MODEM_ACCESS_TECHNOLOGY_1XRTT);
+ EXPECT_CALL(*device_adaptor_, EmitStringChanged(
+ kTechnologyFamilyProperty, kTechnologyFamilyCdma)).
+ Times(1);
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM,
+ modem_properties,
+ vector<string>());
+ Mock::VerifyAndClearExpectations(device_adaptor_);
+
+ // Back to LTE
+ modem_properties.clear();
+ modem_properties[MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES].
+ writer().append_uint32(MM_MODEM_ACCESS_TECHNOLOGY_LTE);
+ EXPECT_CALL(*device_adaptor_, EmitStringChanged(
+ kTechnologyFamilyProperty, kTechnologyFamilyGsm)).
+ Times(1);
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM,
+ modem_properties,
+ vector<string>());
+ Mock::VerifyAndClearExpectations(device_adaptor_);
+
+ // LTE & CDMA - the device adaptor should not be called!
+ modem_properties.clear();
+ modem_properties[MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES].
+ writer().append_uint32(MM_MODEM_ACCESS_TECHNOLOGY_LTE |
+ MM_MODEM_ACCESS_TECHNOLOGY_1XRTT);
+ EXPECT_CALL(*device_adaptor_, EmitStringChanged(_, _)).Times(0);
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM,
+ modem_properties,
+ vector<string>());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, UpdateRegistrationState) {
+ capability_->InitProxies();
+
+ CreateService();
+ cellular_->set_imsi("310240123456789");
+ cellular_->set_modem_state(Cellular::kModemStateConnected);
+ SetRegistrationDroppedUpdateTimeout(0);
+
+ const Stringmap &home_provider_map = cellular_->home_provider();
+ ASSERT_NE(home_provider_map.end(), home_provider_map.find(kOperatorNameKey));
+ string home_provider = home_provider_map.find(kOperatorNameKey)->second;
+ string ota_name = cellular_->service_->friendly_name();
+
+ // Home --> Roaming should be effective immediately.
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING,
+ capability_->registration_state_);
+
+ // Idle --> Roaming should be effective immediately.
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE,
+ home_provider,
+ ota_name);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_IDLE,
+ capability_->registration_state_);
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING,
+ capability_->registration_state_);
+
+ // Idle --> Searching should be effective immediately.
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE,
+ home_provider,
+ ota_name);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_IDLE,
+ capability_->registration_state_);
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ capability_->registration_state_);
+
+ // Home --> Searching --> Home should never see Searching.
+ EXPECT_CALL(*(modem_info_.mock_metrics()),
+ Notify3GPPRegistrationDelayedDropPosted());
+ EXPECT_CALL(*(modem_info_.mock_metrics()),
+ Notify3GPPRegistrationDelayedDropCanceled());
+
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ Mock::VerifyAndClearExpectations(modem_info_.mock_metrics());
+
+ // Home --> Searching --> wait till dispatch should see Searching
+ EXPECT_CALL(*(modem_info_.mock_metrics()),
+ Notify3GPPRegistrationDelayedDropPosted());
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ capability_->registration_state_);
+ Mock::VerifyAndClearExpectations(modem_info_.mock_metrics());
+
+ // Home --> Searching --> Searching --> wait till dispatch should see
+ // Searching *and* the first callback should be cancelled.
+ EXPECT_CALL(*this, DummyCallback()).Times(0);
+ EXPECT_CALL(*(modem_info_.mock_metrics()),
+ Notify3GPPRegistrationDelayedDropPosted());
+
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ home_provider,
+ ota_name);
+ SetMockRegistrationDroppedUpdateCallback();
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ capability_->registration_state_);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, IsRegistered) {
+ capability_->registration_state_ = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE;
+ EXPECT_FALSE(capability_->IsRegistered());
+
+ capability_->registration_state_ = MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
+ EXPECT_TRUE(capability_->IsRegistered());
+
+ capability_->registration_state_ = MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING;
+ EXPECT_FALSE(capability_->IsRegistered());
+
+ capability_->registration_state_ = MM_MODEM_3GPP_REGISTRATION_STATE_DENIED;
+ EXPECT_FALSE(capability_->IsRegistered());
+
+ capability_->registration_state_ = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
+ EXPECT_FALSE(capability_->IsRegistered());
+
+ capability_->registration_state_ = MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING;
+ EXPECT_TRUE(capability_->IsRegistered());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest,
+ UpdateRegistrationStateModemNotConnected) {
+ capability_->InitProxies();
+ CreateService();
+
+ cellular_->set_imsi("310240123456789");
+ cellular_->set_modem_state(Cellular::kModemStateRegistered);
+ SetRegistrationDroppedUpdateTimeout(0);
+
+ const Stringmap &home_provider_map = cellular_->home_provider();
+ ASSERT_NE(home_provider_map.end(), home_provider_map.find(kOperatorNameKey));
+ string home_provider = home_provider_map.find(kOperatorNameKey)->second;
+ string ota_name = cellular_->service_->friendly_name();
+
+ // Home --> Searching should be effective immediately.
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
+ capability_->registration_state_);
+ capability_->On3GPPRegistrationChanged(
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ home_provider,
+ ota_name);
+ EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
+ capability_->registration_state_);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, IsValidSimPath) {
+ // Invalid paths
+ EXPECT_FALSE(capability_->IsValidSimPath(""));
+ EXPECT_FALSE(capability_->IsValidSimPath("/"));
+
+ // A valid path
+ EXPECT_TRUE(capability_->IsValidSimPath(
+ "/org/freedesktop/ModemManager1/SIM/0"));
+
+ // Note that any string that is not one of the above invalid paths is
+ // currently regarded as valid, since the ModemManager spec doesn't impose
+ // a strict format on the path. The validity of this is subject to change.
+ EXPECT_TRUE(capability_->IsValidSimPath("path"));
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, NormalizeMdn) {
+ EXPECT_EQ("", capability_->NormalizeMdn(""));
+ EXPECT_EQ("12345678901", capability_->NormalizeMdn("12345678901"));
+ EXPECT_EQ("12345678901", capability_->NormalizeMdn("+1 234 567 8901"));
+ EXPECT_EQ("12345678901", capability_->NormalizeMdn("+1-234-567-8901"));
+ EXPECT_EQ("12345678901", capability_->NormalizeMdn("+1 (234) 567-8901"));
+ EXPECT_EQ("12345678901", capability_->NormalizeMdn("1 234 567 8901 "));
+ EXPECT_EQ("2345678901", capability_->NormalizeMdn("(234) 567-8901"));
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, SimPathChanged) {
+ // Set up mock modem SIM properties
+ const char kImsi[] = "310100000001";
+ const char kSimIdentifier[] = "9999888";
+ const char kOperatorIdentifier[] = "310240";
+ const char kOperatorName[] = "Custom SPN";
+ DBusPropertiesMap sim_properties;
+ sim_properties[MM_SIM_PROPERTY_IMSI].writer().append_string(kImsi);
+ sim_properties[MM_SIM_PROPERTY_SIMIDENTIFIER].writer()
+ .append_string(kSimIdentifier);
+ sim_properties[MM_SIM_PROPERTY_OPERATORIDENTIFIER].writer()
+ .append_string(kOperatorIdentifier);
+ sim_properties[MM_SIM_PROPERTY_OPERATORNAME].writer()
+ .append_string(kOperatorName);
+
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_SIM))
+ .Times(1).WillOnce(Return(sim_properties));
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .Times(1);
+
+ EXPECT_FALSE(cellular_->sim_present());
+ EXPECT_EQ(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ("", capability_->sim_path_);
+ EXPECT_EQ("", cellular_->imsi());
+ EXPECT_EQ("", cellular_->sim_identifier());
+ EXPECT_EQ("", capability_->spn_);
+
+ capability_->OnSimPathChanged(kSimPath);
+ EXPECT_TRUE(cellular_->sim_present());
+ EXPECT_NE(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ(kSimPath, capability_->sim_path_);
+ EXPECT_EQ(kImsi, cellular_->imsi());
+ EXPECT_EQ(kSimIdentifier, cellular_->sim_identifier());
+ EXPECT_EQ(kOperatorName, capability_->spn_);
+
+ // Changing to the same SIM path should be a no-op.
+ capability_->OnSimPathChanged(kSimPath);
+ EXPECT_TRUE(cellular_->sim_present());
+ EXPECT_NE(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ(kSimPath, capability_->sim_path_);
+ EXPECT_EQ(kImsi, cellular_->imsi());
+ EXPECT_EQ(kSimIdentifier, cellular_->sim_identifier());
+ EXPECT_EQ(kOperatorName, capability_->spn_);
+
+ capability_->OnSimPathChanged("");
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(properties_proxy_.get());
+ EXPECT_FALSE(cellular_->sim_present());
+ EXPECT_EQ(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ("", capability_->sim_path_);
+ EXPECT_EQ("", cellular_->imsi());
+ EXPECT_EQ("", cellular_->sim_identifier());
+ EXPECT_EQ("", capability_->spn_);
+
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_SIM))
+ .Times(1).WillOnce(Return(sim_properties));
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .Times(1);
+
+ capability_->OnSimPathChanged(kSimPath);
+ EXPECT_TRUE(cellular_->sim_present());
+ EXPECT_NE(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ(kSimPath, capability_->sim_path_);
+ EXPECT_EQ(kImsi, cellular_->imsi());
+ EXPECT_EQ(kSimIdentifier, cellular_->sim_identifier());
+ EXPECT_EQ(kOperatorName, capability_->spn_);
+
+ capability_->OnSimPathChanged("/");
+ EXPECT_FALSE(cellular_->sim_present());
+ EXPECT_EQ(nullptr, capability_->sim_proxy_);;
+ EXPECT_EQ("/", capability_->sim_path_);
+ EXPECT_EQ("", cellular_->imsi());
+ EXPECT_EQ("", cellular_->sim_identifier());
+ EXPECT_EQ("", capability_->spn_);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, SimPropertiesChanged) {
+ // Set up mock modem properties
+ DBusPropertiesMap modem_properties;
+ modem_properties[MM_MODEM_PROPERTY_SIM].writer().append_path(kSimPath);
+
+ // Set up mock modem sim properties
+ const char kImsi[] = "310100000001";
+ DBusPropertiesMap sim_properties;
+ sim_properties[MM_SIM_PROPERTY_IMSI].writer().append_string(kImsi);
+
+ EXPECT_CALL(*properties_proxy_, GetAll(MM_DBUS_INTERFACE_SIM))
+ .WillOnce(Return(sim_properties));
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .Times(0);
+
+ EXPECT_FALSE(capability_->sim_proxy_.get());
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_MODEM,
+ modem_properties, vector<string>());
+ EXPECT_EQ(kSimPath, capability_->sim_path_);
+ EXPECT_TRUE(capability_->sim_proxy_.get());
+ EXPECT_EQ(kImsi, cellular_->imsi());
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ // Updating the SIM
+ DBusPropertiesMap new_properties;
+ const char kNewImsi[] = "310240123456789";
+ const char kSimIdentifier[] = "9999888";
+ const char kOperatorIdentifier[] = "310240";
+ const char kOperatorName[] = "Custom SPN";
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .Times(2);
+ EXPECT_CALL(*mock_home_provider_info_, UpdateIMSI(kNewImsi)).Times(2);
+ new_properties[MM_SIM_PROPERTY_IMSI].writer().append_string(kNewImsi);
+ new_properties[MM_SIM_PROPERTY_SIMIDENTIFIER].writer().
+ append_string(kSimIdentifier);
+ new_properties[MM_SIM_PROPERTY_OPERATORIDENTIFIER].writer().
+ append_string(kOperatorIdentifier);
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_SIM,
+ new_properties,
+ vector<string>());
+ EXPECT_EQ(kNewImsi, cellular_->imsi());
+ EXPECT_EQ(kSimIdentifier, cellular_->sim_identifier());
+ EXPECT_EQ("", capability_->spn_);
+
+ new_properties[MM_SIM_PROPERTY_OPERATORNAME].writer().
+ append_string(kOperatorName);
+ capability_->OnDBusPropertiesChanged(MM_DBUS_INTERFACE_SIM,
+ new_properties,
+ vector<string>());
+ EXPECT_EQ(kOperatorName, capability_->spn_);
+}
+
+MATCHER_P(SizeIs, value, "") {
+ return static_cast<size_t>(value) == arg.size();
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, Reset) {
+ // Save pointers to proxies before they are lost by the call to InitProxies
+ mm1::MockModemProxy *modem_proxy = modem_proxy_.get();
+ EXPECT_CALL(*modem_proxy, set_state_changed_callback(_));
+ capability_->InitProxies();
+
+ Error error;
+ ResultCallback reset_callback;
+
+ EXPECT_CALL(*modem_proxy, Reset(_, _, CellularCapability::kTimeoutReset))
+ .WillOnce(SaveArg<1>(&reset_callback));
+
+ capability_->Reset(&error, ResultCallback());
+ EXPECT_TRUE(capability_->resetting_);
+ reset_callback.Run(error);
+ EXPECT_FALSE(capability_->resetting_);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, UpdateActiveBearer) {
+ // Common resources.
+ const size_t kPathCount = 3;
+ DBus::Path active_paths[kPathCount], inactive_paths[kPathCount];
+ for (size_t i = 0; i < kPathCount; ++i) {
+ active_paths[i] = base::StringPrintf("%s/%zu", kActiveBearerPathPrefix, i);
+ inactive_paths[i] =
+ base::StringPrintf("%s/%zu", kInactiveBearerPathPrefix, i);
+ }
+
+ EXPECT_EQ(nullptr, capability_->GetActiveBearer());;
+
+ // Check that |active_bearer_| is set correctly when an active bearer is
+ // returned.
+ capability_->OnBearersChanged({inactive_paths[0],
+ inactive_paths[1],
+ active_paths[2],
+ inactive_paths[1],
+ inactive_paths[2]});
+ capability_->UpdateActiveBearer();
+ ASSERT_NE(nullptr, capability_->GetActiveBearer());;
+ EXPECT_EQ(active_paths[2], capability_->GetActiveBearer()->dbus_path());
+
+ // Check that |active_bearer_| is nullptr if no active bearers are returned.
+ capability_->OnBearersChanged({inactive_paths[0],
+ inactive_paths[1],
+ inactive_paths[2],
+ inactive_paths[1]});
+ capability_->UpdateActiveBearer();
+ EXPECT_EQ(nullptr, capability_->GetActiveBearer());;
+
+ // Check that returning multiple bearers causes death.
+ capability_->OnBearersChanged({active_paths[0],
+ inactive_paths[1],
+ inactive_paths[2],
+ active_paths[1],
+ inactive_paths[1]});
+ EXPECT_DEATH(capability_->UpdateActiveBearer(),
+ "Found more than one active bearer.");
+
+ capability_->OnBearersChanged({});
+ capability_->UpdateActiveBearer();
+ EXPECT_EQ(nullptr, capability_->GetActiveBearer());;
+}
+
+// Validates expected behavior of Connect function
+TEST_F(CellularCapabilityUniversalMainTest, Connect) {
+ mm1::MockModemSimpleProxy *modem_simple_proxy = modem_simple_proxy_.get();
+ SetSimpleProxy();
+ Error error;
+ DBusPropertiesMap properties;
+ capability_->apn_try_list_.clear();
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ DBus::Path bearer("/foo");
+
+ // Test connect failures
+ EXPECT_CALL(*modem_simple_proxy, Connect(_, _, _, _))
+ .WillRepeatedly(SaveArg<2>(&connect_callback_));
+ capability_->Connect(properties, &error, callback);
+ EXPECT_TRUE(error.IsSuccess());
+ EXPECT_CALL(*this, TestCallback(IsFailure()));
+ EXPECT_CALL(*service_, ClearLastGoodApn());
+ connect_callback_.Run(bearer, Error(Error::kOperationFailed));
+ Mock::VerifyAndClearExpectations(this);
+
+ // Test connect success
+ capability_->Connect(properties, &error, callback);
+ EXPECT_TRUE(error.IsSuccess());
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ connect_callback_.Run(bearer, Error(Error::kSuccess));
+ Mock::VerifyAndClearExpectations(this);
+
+ // Test connect failures without a service. Make sure that shill
+ // does not crash if the connect failed and there is no
+ // CellularService object. This can happen if the modem is enabled
+ // and then quickly disabled.
+ cellular_->service_ = nullptr;
+ EXPECT_FALSE(capability_->cellular()->service());
+ capability_->Connect(properties, &error, callback);
+ EXPECT_TRUE(error.IsSuccess());
+ EXPECT_CALL(*this, TestCallback(IsFailure()));
+ connect_callback_.Run(bearer, Error(Error::kOperationFailed));
+}
+
+// Validates Connect iterates over APNs
+TEST_F(CellularCapabilityUniversalMainTest, ConnectApns) {
+ mm1::MockModemSimpleProxy *modem_simple_proxy = modem_simple_proxy_.get();
+ SetSimpleProxy();
+ Error error;
+ DBusPropertiesMap properties;
+ capability_->apn_try_list_.clear();
+ ResultCallback callback =
+ Bind(&CellularCapabilityUniversalTest::TestCallback, Unretained(this));
+ DBus::Path bearer("/bearer0");
+
+ const char apn_name_foo[] = "foo";
+ const char apn_name_bar[] = "bar";
+ EXPECT_CALL(*modem_simple_proxy, Connect(HasApn(apn_name_foo), _, _, _))
+ .WillOnce(SaveArg<2>(&connect_callback_));
+ Stringmap apn1;
+ apn1[kApnProperty] = apn_name_foo;
+ capability_->apn_try_list_.push_back(apn1);
+ Stringmap apn2;
+ apn2[kApnProperty] = apn_name_bar;
+ capability_->apn_try_list_.push_back(apn2);
+ capability_->FillConnectPropertyMap(&properties);
+ capability_->Connect(properties, &error, callback);
+ EXPECT_TRUE(error.IsSuccess());
+ Mock::VerifyAndClearExpectations(modem_simple_proxy);
+
+ EXPECT_CALL(*modem_simple_proxy, Connect(HasApn(apn_name_bar), _, _, _))
+ .WillOnce(SaveArg<2>(&connect_callback_));
+ EXPECT_CALL(*service_, ClearLastGoodApn());
+ connect_callback_.Run(bearer, Error(Error::kInvalidApn));
+
+ EXPECT_CALL(*service_, SetLastGoodApn(apn2));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ connect_callback_.Run(bearer, Error(Error::kSuccess));
+}
+
+// Validates GetTypeString and AccessTechnologyToTechnologyFamily
+TEST_F(CellularCapabilityUniversalMainTest, GetTypeString) {
+ const int gsm_technologies[] = {
+ MM_MODEM_ACCESS_TECHNOLOGY_LTE,
+ MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS,
+ MM_MODEM_ACCESS_TECHNOLOGY_HSPA,
+ MM_MODEM_ACCESS_TECHNOLOGY_HSUPA,
+ MM_MODEM_ACCESS_TECHNOLOGY_HSDPA,
+ MM_MODEM_ACCESS_TECHNOLOGY_UMTS,
+ MM_MODEM_ACCESS_TECHNOLOGY_EDGE,
+ MM_MODEM_ACCESS_TECHNOLOGY_GPRS,
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT,
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM,
+ MM_MODEM_ACCESS_TECHNOLOGY_LTE | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0,
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0,
+ MM_MODEM_ACCESS_TECHNOLOGY_LTE | MM_MODEM_ACCESS_TECHNOLOGY_EVDOA,
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM | MM_MODEM_ACCESS_TECHNOLOGY_EVDOA,
+ MM_MODEM_ACCESS_TECHNOLOGY_LTE | MM_MODEM_ACCESS_TECHNOLOGY_EVDOB,
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM | MM_MODEM_ACCESS_TECHNOLOGY_EVDOB,
+ MM_MODEM_ACCESS_TECHNOLOGY_GSM | MM_MODEM_ACCESS_TECHNOLOGY_1XRTT,
+ };
+ for (size_t i = 0; i < arraysize(gsm_technologies); ++i) {
+ capability_->access_technologies_ = gsm_technologies[i];
+ ASSERT_EQ(capability_->GetTypeString(), kTechnologyFamilyGsm);
+ }
+ const int cdma_technologies[] = {
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDO0,
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDOA,
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDOA | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0,
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDOB,
+ MM_MODEM_ACCESS_TECHNOLOGY_EVDOB | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0,
+ MM_MODEM_ACCESS_TECHNOLOGY_1XRTT,
+ };
+ for (size_t i = 0; i < arraysize(cdma_technologies); ++i) {
+ capability_->access_technologies_ = cdma_technologies[i];
+ ASSERT_EQ(capability_->GetTypeString(), kTechnologyFamilyCdma);
+ }
+ capability_->access_technologies_ = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ ASSERT_EQ(capability_->GetTypeString(), "");
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, AllowRoaming) {
+ EXPECT_FALSE(cellular_->allow_roaming_);
+ EXPECT_FALSE(cellular_->provider_requires_roaming());
+ EXPECT_FALSE(capability_->AllowRoaming());
+ cellular_->set_provider_requires_roaming(true);
+ EXPECT_TRUE(capability_->AllowRoaming());
+ cellular_->set_provider_requires_roaming(false);
+ cellular_->allow_roaming_ = true;
+ EXPECT_TRUE(capability_->AllowRoaming());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, GetMdnForOLP) {
+ const string kVzwUUID = "c83d6597-dc91-4d48-a3a7-d86b80123751";
+ const string kFooUUID = "foo";
+ MockMobileOperatorInfo mock_operator_info(&dispatcher_,
+ "MobileOperatorInfo");
+
+ mock_operator_info.SetEmptyDefaultsForProperties();
+ EXPECT_CALL(mock_operator_info, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mock_operator_info, uuid()).WillRepeatedly(ReturnRef(kVzwUUID));
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnknown;
+
+ cellular_->set_mdn("");
+ EXPECT_EQ("0000000000", capability_->GetMdnForOLP(&mock_operator_info));
+ cellular_->set_mdn("0123456789");
+ EXPECT_EQ("0123456789", capability_->GetMdnForOLP(&mock_operator_info));
+ cellular_->set_mdn("10123456789");
+ EXPECT_EQ("0123456789", capability_->GetMdnForOLP(&mock_operator_info));
+
+ cellular_->set_mdn("1021232333");
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnprovisioned;
+ EXPECT_EQ("0000000000", capability_->GetMdnForOLP(&mock_operator_info));
+ Mock::VerifyAndClearExpectations(&mock_operator_info);
+
+ mock_operator_info.SetEmptyDefaultsForProperties();
+ EXPECT_CALL(mock_operator_info, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mock_operator_info, uuid()).WillRepeatedly(ReturnRef(kFooUUID));
+
+ cellular_->set_mdn("");
+ EXPECT_EQ("", capability_->GetMdnForOLP(&mock_operator_info));
+ cellular_->set_mdn("0123456789");
+ EXPECT_EQ("0123456789", capability_->GetMdnForOLP(&mock_operator_info));
+ cellular_->set_mdn("10123456789");
+ EXPECT_EQ("10123456789", capability_->GetMdnForOLP(&mock_operator_info));
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, UpdateServiceOLP) {
+ const MobileOperatorInfo::OnlinePortal kOlp {
+ "http://testurl",
+ "POST",
+ "imei=${imei}&imsi=${imsi}&mdn=${mdn}&min=${min}&iccid=${iccid}"};
+ const vector<MobileOperatorInfo::OnlinePortal> kOlpList {kOlp};
+ const string kUuidVzw = "c83d6597-dc91-4d48-a3a7-d86b80123751";
+ const string kUuidFoo = "foo";
+
+ cellular_->set_imei("1");
+ cellular_->set_imsi("2");
+ cellular_->set_mdn("10123456789");
+ cellular_->set_min("5");
+ cellular_->set_sim_identifier("6");
+
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, olp_list())
+ .WillRepeatedly(ReturnRef(kOlpList));
+ EXPECT_CALL(*mock_home_provider_info_, uuid())
+ .WillOnce(ReturnRef(kUuidVzw));
+ CreateService();
+ capability_->UpdateServiceOLP();
+ // Copy to simplify assertions below.
+ Stringmap vzw_olp = cellular_->service()->olp();
+ EXPECT_EQ("http://testurl", vzw_olp[kPaymentPortalURL]);
+ EXPECT_EQ("POST", vzw_olp[kPaymentPortalMethod]);
+ EXPECT_EQ("imei=1&imsi=2&mdn=0123456789&min=5&iccid=6",
+ vzw_olp[kPaymentPortalPostData]);
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, olp_list())
+ .WillRepeatedly(ReturnRef(kOlpList));
+ EXPECT_CALL(*mock_home_provider_info_, uuid())
+ .WillOnce(ReturnRef(kUuidFoo));
+ capability_->UpdateServiceOLP();
+ // Copy to simplify assertions below.
+ Stringmap olp = cellular_->service()->olp();
+ EXPECT_EQ("http://testurl", olp[kPaymentPortalURL]);
+ EXPECT_EQ("POST", olp[kPaymentPortalMethod]);
+ EXPECT_EQ("imei=1&imsi=2&mdn=10123456789&min=5&iccid=6",
+ olp[kPaymentPortalPostData]);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, IsMdnValid) {
+ cellular_->set_mdn("");
+ EXPECT_FALSE(capability_->IsMdnValid());
+ cellular_->set_mdn("0000000");
+ EXPECT_FALSE(capability_->IsMdnValid());
+ cellular_->set_mdn("0000001");
+ EXPECT_TRUE(capability_->IsMdnValid());
+ cellular_->set_mdn("1231223");
+ EXPECT_TRUE(capability_->IsMdnValid());
+}
+
+TEST_F(CellularCapabilityUniversalTimerTest, CompleteActivation) {
+ const char kIccid[] = "1234567";
+
+ cellular_->set_sim_identifier(kIccid);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ SetActivationState(PendingActivationStore::kIdentifierICCID,
+ kIccid,
+ PendingActivationStore::kStatePending))
+ .Times(1);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID,
+ kIccid))
+ .WillOnce(Return(PendingActivationStore::kStatePending));
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivating))
+ .Times(1);
+ EXPECT_CALL(*modem_proxy_.get(), Reset(_, _, _)).Times(1);
+ Error error;
+ capability_->InitProxies();
+ capability_->CompleteActivation(&error);
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ Mock::VerifyAndClearExpectations(service_);
+ Mock::VerifyAndClearExpectations(&mock_dispatcher_);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, UpdateServiceActivationState) {
+ const char kIccid[] = "1234567";
+ const vector<MobileOperatorInfo::OnlinePortal> olp_list {
+ {"some@url", "some_method", "some_post_data"}
+ };
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnprovisioned;
+ cellular_->set_sim_identifier("");
+ cellular_->set_mdn("0000000000");
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, olp_list())
+ .WillRepeatedly(ReturnRef(olp_list));
+
+ service_->SetAutoConnect(false);
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateNotActivated))
+ .Times(1);
+ capability_->UpdateServiceActivationState();
+ Mock::VerifyAndClearExpectations(service_);
+ EXPECT_FALSE(service_->auto_connect());
+
+ cellular_->set_mdn("1231231122");
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnknown;
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivated))
+ .Times(1);
+ capability_->UpdateServiceActivationState();
+ Mock::VerifyAndClearExpectations(service_);
+ EXPECT_TRUE(service_->auto_connect());
+
+ // Make sure we don't overwrite auto-connect if a service is already
+ // activated before calling UpdateServiceActivationState().
+ service_->SetAutoConnect(false);
+ EXPECT_FALSE(service_->auto_connect());
+ const string activation_state = kActivationStateActivated;
+ EXPECT_CALL(*service_, activation_state())
+ .WillOnce(ReturnRef(activation_state));
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivated))
+ .Times(1);
+ capability_->UpdateServiceActivationState();
+ Mock::VerifyAndClearExpectations(service_);
+ EXPECT_FALSE(service_->auto_connect());
+
+ service_->SetAutoConnect(false);
+ cellular_->set_mdn("0000000000");
+ cellular_->set_sim_identifier(kIccid);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID,
+ kIccid))
+ .Times(1)
+ .WillRepeatedly(Return(PendingActivationStore::kStatePending));
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivating))
+ .Times(1);
+ capability_->UpdateServiceActivationState();
+ Mock::VerifyAndClearExpectations(service_);
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ EXPECT_FALSE(service_->auto_connect());
+
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID,
+ kIccid))
+ .Times(2)
+ .WillRepeatedly(Return(PendingActivationStore::kStateActivated));
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivated))
+ .Times(1);
+ capability_->UpdateServiceActivationState();
+ Mock::VerifyAndClearExpectations(service_);
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+ EXPECT_TRUE(service_->auto_connect());
+
+ // SubscriptionStateUnprovisioned overrides valid MDN.
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnprovisioned;
+ cellular_->set_mdn("1231231122");
+ cellular_->set_sim_identifier("");
+ service_->SetAutoConnect(false);
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateNotActivated))
+ .Times(1);
+ capability_->UpdateServiceActivationState();
+ Mock::VerifyAndClearExpectations(service_);
+ EXPECT_FALSE(service_->auto_connect());
+
+ // SubscriptionStateProvisioned overrides invalid MDN.
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateProvisioned;
+ cellular_->set_mdn("0000000000");
+ cellular_->set_sim_identifier("");
+ service_->SetAutoConnect(false);
+ EXPECT_CALL(*service_, SetActivationState(kActivationStateActivated))
+ .Times(1);
+ capability_->UpdateServiceActivationState();
+ Mock::VerifyAndClearExpectations(service_);
+ EXPECT_TRUE(service_->auto_connect());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, UpdatePendingActivationState) {
+ const char kIccid[] = "1234567";
+
+ capability_->InitProxies();
+ capability_->registration_state_ =
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING;
+
+ // No MDN, no ICCID.
+ cellular_->set_mdn("0000000");
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnknown;
+ cellular_->set_sim_identifier("");
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .Times(0);
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ // Valid MDN, but subsciption_state_ Unprovisioned
+ cellular_->set_mdn("1234567");
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnprovisioned;
+ cellular_->set_sim_identifier("");
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID, _))
+ .Times(0);
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ // ICCID known.
+ cellular_->set_sim_identifier(kIccid);
+
+ // After the modem has reset.
+ capability_->reset_done_ = true;
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID,
+ kIccid))
+ .Times(1).WillOnce(Return(PendingActivationStore::kStatePending));
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ SetActivationState(PendingActivationStore::kIdentifierICCID,
+ kIccid,
+ PendingActivationStore::kStateActivated))
+ .Times(1);
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ // Not registered.
+ capability_->registration_state_ =
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING;
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID,
+ kIccid))
+ .Times(2).WillRepeatedly(Return(PendingActivationStore::kStateActivated));
+ EXPECT_CALL(*service_, AutoConnect()).Times(0);
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(service_);
+
+ // Service, registered.
+ capability_->registration_state_ =
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
+ EXPECT_CALL(*service_, AutoConnect()).Times(1);
+ capability_->UpdatePendingActivationState();
+
+ cellular_->service_->activation_state_ = kActivationStateNotActivated;
+
+ Mock::VerifyAndClearExpectations(service_);
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ // Device is connected.
+ cellular_->state_ = Cellular::kStateConnected;
+ capability_->UpdatePendingActivationState();
+
+ // Device is linked.
+ cellular_->state_ = Cellular::kStateLinked;
+ capability_->UpdatePendingActivationState();
+
+ // Got valid MDN, subscription_state_ is kSubscriptionStateUnknown
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ RemoveEntry(PendingActivationStore::kIdentifierICCID, kIccid));
+ cellular_->state_ = Cellular::kStateRegistered;
+ cellular_->set_mdn("1020304");
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnknown;
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+
+ // Got invalid MDN, subscription_state_ is kSubscriptionStateProvisioned
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ RemoveEntry(PendingActivationStore::kIdentifierICCID, kIccid));
+ cellular_->state_ = Cellular::kStateRegistered;
+ cellular_->set_mdn("0000000");
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateProvisioned;
+ capability_->UpdatePendingActivationState();
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, IsServiceActivationRequired) {
+ const vector<MobileOperatorInfo::OnlinePortal> empty_list;
+ const vector<MobileOperatorInfo::OnlinePortal> olp_list {
+ {"some@url", "some_method", "some_post_data"}
+ };
+
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateProvisioned;
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnprovisioned;
+ EXPECT_TRUE(capability_->IsServiceActivationRequired());
+
+ capability_->subscription_state_ =
+ CellularCapabilityUniversal::kSubscriptionStateUnknown;
+ cellular_->set_mdn("0000000000");
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, olp_list())
+ .WillRepeatedly(ReturnRef(empty_list));
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+
+ // Set expectations for all subsequent cases.
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, olp_list())
+ .WillRepeatedly(ReturnRef(olp_list));
+
+ cellular_->set_mdn("");
+ EXPECT_TRUE(capability_->IsServiceActivationRequired());
+ cellular_->set_mdn("1234567890");
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+ cellular_->set_mdn("0000000000");
+ EXPECT_TRUE(capability_->IsServiceActivationRequired());
+
+ const char kIccid[] = "1234567890";
+ cellular_->set_sim_identifier(kIccid);
+ EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+ GetActivationState(PendingActivationStore::kIdentifierICCID,
+ kIccid))
+ .WillOnce(Return(PendingActivationStore::kStateActivated))
+ .WillOnce(Return(PendingActivationStore::kStatePending))
+ .WillOnce(Return(PendingActivationStore::kStateUnknown));
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+ EXPECT_FALSE(capability_->IsServiceActivationRequired());
+ EXPECT_TRUE(capability_->IsServiceActivationRequired());
+ Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, OnModemCurrentCapabilitiesChanged) {
+ EXPECT_FALSE(cellular_->scanning_supported());
+ capability_->OnModemCurrentCapabilitiesChanged(MM_MODEM_CAPABILITY_LTE);
+ EXPECT_FALSE(cellular_->scanning_supported());
+ capability_->OnModemCurrentCapabilitiesChanged(MM_MODEM_CAPABILITY_CDMA_EVDO);
+ EXPECT_FALSE(cellular_->scanning_supported());
+ capability_->OnModemCurrentCapabilitiesChanged(MM_MODEM_CAPABILITY_GSM_UMTS);
+ EXPECT_TRUE(cellular_->scanning_supported());
+ capability_->OnModemCurrentCapabilitiesChanged(
+ MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO);
+ EXPECT_TRUE(cellular_->scanning_supported());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, GetNetworkTechnologyStringOnE362) {
+ cellular_->set_model_id("");;
+ capability_->access_technologies_ = 0;
+ EXPECT_TRUE(capability_->GetNetworkTechnologyString().empty());
+
+ cellular_->set_mm_plugin(CellularCapabilityUniversal::kNovatelLTEMMPlugin);
+ EXPECT_EQ(kNetworkTechnologyLte, capability_->GetNetworkTechnologyString());
+
+ capability_->access_technologies_ = MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
+ EXPECT_EQ(kNetworkTechnologyLte, capability_->GetNetworkTechnologyString());
+
+ cellular_->set_mm_plugin("");
+ EXPECT_EQ(kNetworkTechnologyGprs, capability_->GetNetworkTechnologyString());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, GetOutOfCreditsDetectionType) {
+ cellular_->set_model_id("");;
+ EXPECT_EQ(OutOfCreditsDetector::OOCTypeNone,
+ capability_->GetOutOfCreditsDetectionType());
+ cellular_->set_mm_plugin(CellularCapabilityUniversal::kAltairLTEMMPlugin);
+ EXPECT_EQ(OutOfCreditsDetector::OOCTypeSubscriptionState,
+ capability_->GetOutOfCreditsDetectionType());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, SimLockStatusToProperty) {
+ Error error;
+ KeyValueStore store = capability_->SimLockStatusToProperty(&error);
+ EXPECT_FALSE(store.GetBool(kSIMLockEnabledProperty));
+ EXPECT_TRUE(store.GetString(kSIMLockTypeProperty).empty());
+ EXPECT_EQ(0, store.GetUint(kSIMLockRetriesLeftProperty));
+
+ capability_->sim_lock_status_.enabled = true;
+ capability_->sim_lock_status_.retries_left = 3;
+ capability_->sim_lock_status_.lock_type = MM_MODEM_LOCK_SIM_PIN;
+ store = capability_->SimLockStatusToProperty(&error);
+ EXPECT_TRUE(store.GetBool(kSIMLockEnabledProperty));
+ EXPECT_EQ("sim-pin", store.GetString(kSIMLockTypeProperty));
+ EXPECT_EQ(3, store.GetUint(kSIMLockRetriesLeftProperty));
+
+ capability_->sim_lock_status_.lock_type = MM_MODEM_LOCK_SIM_PUK;
+ store = capability_->SimLockStatusToProperty(&error);
+ EXPECT_EQ("sim-puk", store.GetString(kSIMLockTypeProperty));
+
+ capability_->sim_lock_status_.lock_type = MM_MODEM_LOCK_SIM_PIN2;
+ store = capability_->SimLockStatusToProperty(&error);
+ EXPECT_TRUE(store.GetString(kSIMLockTypeProperty).empty());
+
+ capability_->sim_lock_status_.lock_type = MM_MODEM_LOCK_SIM_PUK2;
+ store = capability_->SimLockStatusToProperty(&error);
+ EXPECT_TRUE(store.GetString(kSIMLockTypeProperty).empty());
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, OnLockRetriesChanged) {
+ CellularCapabilityUniversal::LockRetryData data;
+ const uint32_t kDefaultRetries = 999;
+
+ capability_->OnLockRetriesChanged(data);
+ EXPECT_EQ(kDefaultRetries, capability_->sim_lock_status_.retries_left);
+
+ data[MM_MODEM_LOCK_SIM_PIN] = 3;
+ data[MM_MODEM_LOCK_SIM_PUK] = 10;
+ capability_->OnLockRetriesChanged(data);
+ EXPECT_EQ(3, capability_->sim_lock_status_.retries_left);
+
+ capability_->sim_lock_status_.lock_type = MM_MODEM_LOCK_SIM_PUK;
+ capability_->OnLockRetriesChanged(data);
+ EXPECT_EQ(10, capability_->sim_lock_status_.retries_left);
+
+ capability_->sim_lock_status_.lock_type = MM_MODEM_LOCK_SIM_PIN;
+ capability_->OnLockRetriesChanged(data);
+ EXPECT_EQ(3, capability_->sim_lock_status_.retries_left);
+
+ data.clear();
+ capability_->OnLockRetriesChanged(data);
+ EXPECT_EQ(kDefaultRetries, capability_->sim_lock_status_.retries_left);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, OnLockTypeChanged) {
+ EXPECT_EQ(MM_MODEM_LOCK_UNKNOWN, capability_->sim_lock_status_.lock_type);
+
+ capability_->OnLockTypeChanged(MM_MODEM_LOCK_NONE);
+ EXPECT_EQ(MM_MODEM_LOCK_NONE, capability_->sim_lock_status_.lock_type);
+ EXPECT_FALSE(capability_->sim_lock_status_.enabled);
+
+ capability_->OnLockTypeChanged(MM_MODEM_LOCK_SIM_PIN);
+ EXPECT_EQ(MM_MODEM_LOCK_SIM_PIN, capability_->sim_lock_status_.lock_type);
+ EXPECT_TRUE(capability_->sim_lock_status_.enabled);
+
+ capability_->sim_lock_status_.enabled = false;
+ capability_->OnLockTypeChanged(MM_MODEM_LOCK_SIM_PUK);
+ EXPECT_EQ(MM_MODEM_LOCK_SIM_PUK, capability_->sim_lock_status_.lock_type);
+ EXPECT_TRUE(capability_->sim_lock_status_.enabled);
+}
+
+TEST_F(CellularCapabilityUniversalMainTest, OnSimLockPropertiesChanged) {
+ EXPECT_EQ(MM_MODEM_LOCK_UNKNOWN, capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(0, capability_->sim_lock_status_.retries_left);
+
+ DBusPropertiesMap changed;
+ vector<string> invalidated;
+
+ capability_->OnModemPropertiesChanged(changed, invalidated);
+ EXPECT_EQ(MM_MODEM_LOCK_UNKNOWN, capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(0, capability_->sim_lock_status_.retries_left);
+
+ ::DBus::Variant variant;
+ ::DBus::MessageIter writer = variant.writer();
+
+ // Unlock retries changed, but the SIM wasn't locked.
+ CellularCapabilityUniversal::LockRetryData retry_data;
+ retry_data[MM_MODEM_LOCK_SIM_PIN] = 3;
+ writer << retry_data;
+ changed[MM_MODEM_PROPERTY_UNLOCKRETRIES] = variant;
+
+ capability_->OnModemPropertiesChanged(changed, invalidated);
+ EXPECT_EQ(MM_MODEM_LOCK_UNKNOWN, capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(3, capability_->sim_lock_status_.retries_left);
+
+ // Unlock retries changed and the SIM got locked.
+ variant.clear();
+ writer = variant.writer();
+ writer << static_cast<uint32_t>(MM_MODEM_LOCK_SIM_PIN);
+ changed[MM_MODEM_PROPERTY_UNLOCKREQUIRED] = variant;
+ capability_->OnModemPropertiesChanged(changed, invalidated);
+ EXPECT_EQ(MM_MODEM_LOCK_SIM_PIN, capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(3, capability_->sim_lock_status_.retries_left);
+
+ // Only unlock retries changed.
+ changed.erase(MM_MODEM_PROPERTY_UNLOCKREQUIRED);
+ retry_data[MM_MODEM_LOCK_SIM_PIN] = 2;
+ variant.clear();
+ writer = variant.writer();
+ writer << retry_data;
+ changed[MM_MODEM_PROPERTY_UNLOCKRETRIES] = variant;
+ capability_->OnModemPropertiesChanged(changed, invalidated);
+ EXPECT_EQ(MM_MODEM_LOCK_SIM_PIN, capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(2, capability_->sim_lock_status_.retries_left);
+
+ // Unlock retries changed with a value that doesn't match the current
+ // lock type. Default to whatever count is available.
+ retry_data.clear();
+ retry_data[MM_MODEM_LOCK_SIM_PIN2] = 2;
+ variant.clear();
+ writer = variant.writer();
+ writer << retry_data;
+ changed[MM_MODEM_PROPERTY_UNLOCKRETRIES] = variant;
+ capability_->OnModemPropertiesChanged(changed, invalidated);
+ EXPECT_EQ(MM_MODEM_LOCK_SIM_PIN, capability_->sim_lock_status_.lock_type);
+ EXPECT_EQ(2, capability_->sim_lock_status_.retries_left);
+}
+
+} // namespace shill
diff --git a/cellular/cellular_error.cc b/cellular/cellular_error.cc
new file mode 100644
index 0000000..ffa44b2
--- /dev/null
+++ b/cellular/cellular_error.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 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/cellular_error.h"
+
+#include <string>
+
+#include <mm/mm-modem.h>
+
+using std::string;
+
+#define MM_MODEM_ERROR(error) MM_MODEM_INTERFACE "." error
+#define MM_MOBILE_ERROR(error) MM_MODEM_GSM_INTERFACE "." error
+
+namespace shill {
+
+static const char *kErrorIncorrectPassword =
+ MM_MOBILE_ERROR(MM_ERROR_MODEM_GSM_INCORRECTPASSWORD);
+static const char *kErrorSimPinRequired =
+ MM_MOBILE_ERROR(MM_ERROR_MODEM_GSM_SIMPINREQUIRED);
+static const char *kErrorSimPukRequired =
+ MM_MOBILE_ERROR(MM_ERROR_MODEM_GSM_SIMPUKREQUIRED);
+static const char *kErrorGprsNotSubscribed =
+ MM_MOBILE_ERROR(MM_ERROR_MODEM_GSM_GPRSNOTSUBSCRIBED);
+
+// static
+void CellularError::FromDBusError(const DBus::Error &dbus_error,
+ Error *error) {
+ if (!error)
+ return;
+
+ if (!dbus_error.is_set()) {
+ error->Reset();
+ return;
+ }
+
+ string name(dbus_error.name());
+ const char *msg = dbus_error.message();
+ Error::Type type;
+
+ if (name == kErrorIncorrectPassword)
+ type = Error::kIncorrectPin;
+ else if (name == kErrorSimPinRequired)
+ type = Error::kPinRequired;
+ else if (name == kErrorSimPukRequired)
+ type = Error::kPinBlocked;
+ else if (name == kErrorGprsNotSubscribed)
+ type = Error::kInvalidApn;
+ else
+ type = Error::kOperationFailed;
+
+ if (msg)
+ return error->Populate(type, msg);
+ else
+ return error->Populate(type);
+}
+
+} // namespace shill
diff --git a/cellular/cellular_error.h b/cellular/cellular_error.h
new file mode 100644
index 0000000..12a7554
--- /dev/null
+++ b/cellular/cellular_error.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_ERROR_H_
+#define SHILL_CELLULAR_CELLULAR_ERROR_H_
+
+#include <dbus-c++/error.h>
+
+#include "shill/error.h"
+
+namespace shill {
+
+class CellularError {
+ public:
+ static void FromDBusError(const DBus::Error &dbus_error, Error *error);
+
+ static void FromMM1DBusError(const DBus::Error &dbus_error, Error *error);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CellularError);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_ERROR_H_
diff --git a/cellular/cellular_error_mm1.cc b/cellular/cellular_error_mm1.cc
new file mode 100644
index 0000000..ef8faf8
--- /dev/null
+++ b/cellular/cellular_error_mm1.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2013 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/cellular_error.h"
+
+#include <string>
+
+#include <ModemManager/ModemManager.h>
+
+// TODO(armansito): Once we refactor the code to handle the ModemManager D-Bus
+// bindings in a dedicated class, this code should move there.
+// (See crbug.com/246425)
+
+using std::string;
+
+namespace shill {
+
+namespace {
+
+const char *kErrorGprsMissingOrUnknownApn =
+ MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX ".GprsMissingOrUnknownApn";
+
+const char *kErrorGprsServiceOptionNotSubscribed =
+ MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX ".GprsServiceOptionNotSubscribed";
+
+const char *kErrorIncorrectPassword =
+ MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX ".IncorrectPassword";
+
+const char *kErrorSimPin =
+ MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX ".SimPin";
+
+const char *kErrorSimPuk =
+ MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX ".SimPuk";
+
+const char* kErrorWrongState = MM_CORE_ERROR_DBUS_PREFIX ".WrongState";
+
+} // namespace
+
+// static
+void CellularError::FromMM1DBusError(const DBus::Error &dbus_error,
+ Error *error) {
+ if (!error)
+ return;
+
+ if (!dbus_error.is_set()) {
+ error->Reset();
+ return;
+ }
+
+ string name(dbus_error.name());
+ const char *msg = dbus_error.message();
+ Error::Type type;
+
+ if (name == kErrorIncorrectPassword)
+ type = Error::kIncorrectPin;
+ else if (name == kErrorSimPin)
+ type = Error::kPinRequired;
+ else if (name == kErrorSimPuk)
+ type = Error::kPinBlocked;
+ else if (name == kErrorGprsMissingOrUnknownApn)
+ type = Error::kInvalidApn;
+ else if (name == kErrorGprsServiceOptionNotSubscribed)
+ type = Error::kInvalidApn;
+ else if (name == kErrorWrongState)
+ type = Error::kWrongState;
+ else
+ type = Error::kOperationFailed;
+
+ if (msg)
+ return error->Populate(type, msg);
+ else
+ return error->Populate(type);
+}
+
+} // namespace shill
diff --git a/cellular/cellular_error_unittest.cc b/cellular/cellular_error_unittest.cc
new file mode 100644
index 0000000..ab15362
--- /dev/null
+++ b/cellular/cellular_error_unittest.cc
@@ -0,0 +1,136 @@
+// Copyright (c) 2013 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/cellular_error.h"
+
+#include <dbus-c++/error.h>
+#include <gtest/gtest.h>
+
+namespace shill {
+
+class CellularErrorTest : public testing::Test {
+};
+
+namespace {
+
+const char kErrorIncorrectPasswordMM[] =
+ "org.freedesktop.ModemManager.Modem.Gsm.IncorrectPassword";
+
+const char kErrorSimPinRequiredMM[] =
+ "org.freedesktop.ModemManager.Modem.Gsm.SimPinRequired";
+
+const char kErrorSimPukRequiredMM[] =
+ "org.freedesktop.ModemManager.Modem.Gsm.SimPukRequired";
+
+const char kErrorGprsNotSubscribedMM[] =
+ "org.freedesktop.ModemManager.Modem.Gsm.GprsNotSubscribed";
+
+const char kErrorIncorrectPasswordMM1[] =
+ "org.freedesktop.ModemManager1.Error.MobileEquipment.IncorrectPassword";
+
+const char kErrorSimPinMM1[] =
+ "org.freedesktop.ModemManager1.Error.MobileEquipment.SimPin";
+
+const char kErrorSimPukMM1[] =
+ "org.freedesktop.ModemManager1.Error.MobileEquipment.SimPuk";
+
+const char kErrorGprsNotSubscribedMM1[] =
+ "org.freedesktop.ModemManager1.Error.MobileEquipment."
+ "GprsServiceOptionNotSubscribed";
+
+const char kErrorWrongStateMM1[] =
+ "org.freedesktop.ModemManager1.Error.Core.WrongState";
+
+
+const char kErrorMessage[] = "Some error message.";
+
+} // namespace
+
+TEST_F(CellularErrorTest, FromDBusError) {
+ Error shill_error;
+
+ CellularError::FromDBusError(DBus::Error(), nullptr);
+ EXPECT_TRUE(shill_error.IsSuccess());
+
+ CellularError::FromDBusError(DBus::Error(), &shill_error);
+ EXPECT_TRUE(shill_error.IsSuccess());
+
+ CellularError::FromDBusError(
+ DBus::Error(kErrorIncorrectPasswordMM, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kIncorrectPin, shill_error.type());
+
+ CellularError::FromDBusError(
+ DBus::Error(kErrorSimPinRequiredMM, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kPinRequired, shill_error.type());
+
+ CellularError::FromDBusError(
+ DBus::Error(kErrorSimPukRequiredMM, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kPinBlocked, shill_error.type());
+
+ CellularError::FromDBusError(
+ DBus::Error(kErrorGprsNotSubscribedMM, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kInvalidApn, shill_error.type());
+
+ CellularError::FromDBusError(
+ DBus::Error(kErrorIncorrectPasswordMM1, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kOperationFailed, shill_error.type());
+
+ CellularError::FromDBusError(
+ DBus::Error("Some random error name.", kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kOperationFailed, shill_error.type());
+}
+
+TEST_F(CellularErrorTest, FromMM1DBusError) {
+ Error shill_error;
+
+ CellularError::FromDBusError(DBus::Error(), nullptr);
+ EXPECT_TRUE(shill_error.IsSuccess());
+
+ CellularError::FromMM1DBusError(DBus::Error(), &shill_error);
+ EXPECT_TRUE(shill_error.IsSuccess());
+
+ CellularError::FromMM1DBusError(
+ DBus::Error(kErrorIncorrectPasswordMM1, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kIncorrectPin, shill_error.type());
+
+ CellularError::FromMM1DBusError(
+ DBus::Error(kErrorSimPinMM1, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kPinRequired, shill_error.type());
+
+ CellularError::FromMM1DBusError(
+ DBus::Error(kErrorSimPukMM1, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kPinBlocked, shill_error.type());
+
+ CellularError::FromMM1DBusError(
+ DBus::Error(kErrorGprsNotSubscribedMM1, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kInvalidApn, shill_error.type());
+
+ CellularError::FromMM1DBusError(
+ DBus::Error(kErrorWrongStateMM1, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kWrongState, shill_error.type());
+
+ CellularError::FromMM1DBusError(
+ DBus::Error(kErrorIncorrectPasswordMM, kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kOperationFailed, shill_error.type());
+
+ CellularError::FromMM1DBusError(
+ DBus::Error("Some random error name.", kErrorMessage),
+ &shill_error);
+ EXPECT_EQ(Error::kOperationFailed, shill_error.type());
+}
+
+} // namespace shill
+
diff --git a/cellular/cellular_service.cc b/cellular/cellular_service.cc
new file mode 100644
index 0000000..32e835b
--- /dev/null
+++ b/cellular/cellular_service.cc
@@ -0,0 +1,456 @@
+// Copyright (c) 2012 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/cellular_service.h"
+
+#include <string>
+
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/adaptor_interfaces.h"
+#include "shill/cellular/cellular.h"
+#include "shill/property_accessor.h"
+#include "shill/store_interface.h"
+
+using std::string;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularService *c) { return c->GetRpcIdentifier(); }
+}
+
+// statics
+const char CellularService::kAutoConnActivating[] = "activating";
+const char CellularService::kAutoConnBadPPPCredentials[] =
+ "bad PPP credentials";
+const char CellularService::kAutoConnDeviceDisabled[] = "device disabled";
+const char CellularService::kAutoConnOutOfCredits[] = "device out of credits";
+const char CellularService::kAutoConnOutOfCreditsDetectionInProgress[] =
+ "device detecting out-of-credits";
+const char CellularService::kStoragePPPUsername[] = "Cellular.PPP.Username";
+const char CellularService::kStoragePPPPassword[] = "Cellular.PPP.Password";
+
+// TODO(petkov): Add these to system_api/dbus/service_constants.h
+namespace {
+const char kCellularPPPUsernameProperty[] = "Cellular.PPP.Username";
+const char kCellularPPPPasswordProperty[] = "Cellular.PPP.Password";
+} // namespace
+
+namespace {
+const char kStorageAPN[] = "Cellular.APN";
+const char kStorageLastGoodAPN[] = "Cellular.LastGoodAPN";
+} // namespace
+
+static bool GetNonEmptyField(const Stringmap &stringmap,
+ const string &fieldname,
+ string *value) {
+ Stringmap::const_iterator it = stringmap.find(fieldname);
+ if (it != stringmap.end() && !it->second.empty()) {
+ *value = it->second;
+ return true;
+ }
+ return false;
+}
+
+CellularService::CellularService(ModemInfo *modem_info,
+ const CellularRefPtr &device)
+ : Service(modem_info->control_interface(), modem_info->dispatcher(),
+ modem_info->metrics(), modem_info->manager(),
+ Technology::kCellular),
+ weak_ptr_factory_(this),
+ activation_type_(kActivationTypeUnknown),
+ cellular_(device),
+ is_auto_connecting_(false) {
+ SetConnectable(true);
+ PropertyStore *store = this->mutable_store();
+ HelpRegisterDerivedString(kActivationTypeProperty,
+ &CellularService::CalculateActivationType,
+ nullptr);
+ store->RegisterConstString(kActivationStateProperty, &activation_state_);
+ HelpRegisterDerivedStringmap(kCellularApnProperty,
+ &CellularService::GetApn,
+ &CellularService::SetApn);
+ store->RegisterConstStringmap(kCellularLastGoodApnProperty,
+ &last_good_apn_info_);
+ store->RegisterConstString(kNetworkTechnologyProperty, &network_technology_);
+ HelpRegisterDerivedBool(kOutOfCreditsProperty,
+ &CellularService::IsOutOfCredits,
+ nullptr);
+ store->RegisterConstStringmap(kPaymentPortalProperty, &olp_);
+ store->RegisterConstString(kRoamingStateProperty, &roaming_state_);
+ store->RegisterConstStringmap(kServingOperatorProperty, &serving_operator_);
+ store->RegisterConstString(kUsageURLProperty, &usage_url_);
+ store->RegisterString(kCellularPPPUsernameProperty, &ppp_username_);
+ store->RegisterWriteOnlyString(kCellularPPPPasswordProperty, &ppp_password_);
+
+ set_friendly_name(cellular_->CreateDefaultFriendlyServiceName());
+ SetStorageIdentifier(string(kTypeCellular) + "_" +
+ cellular_->address() + "_" + friendly_name());
+ // Assume we are not performing any out-of-credits detection.
+ // The capability can reinitialize with the appropriate type later.
+ InitOutOfCreditsDetection(OutOfCreditsDetector::OOCTypeNone);
+}
+
+CellularService::~CellularService() { }
+
+bool CellularService::IsAutoConnectable(const char **reason) const {
+ if (!cellular_->running()) {
+ *reason = kAutoConnDeviceDisabled;
+ return false;
+ }
+ if (cellular_->IsActivating()) {
+ *reason = kAutoConnActivating;
+ return false;
+ }
+ if (failure() == kFailurePPPAuth) {
+ *reason = kAutoConnBadPPPCredentials;
+ return false;
+ }
+ if (out_of_credits_detector_->IsDetecting()) {
+ *reason = kAutoConnOutOfCreditsDetectionInProgress;
+ return false;
+ }
+ if (out_of_credits_detector_->out_of_credits()) {
+ *reason = kAutoConnOutOfCredits;
+ return false;
+ }
+ return Service::IsAutoConnectable(reason);
+}
+
+void CellularService::HelpRegisterDerivedString(
+ const string &name,
+ string(CellularService::*get)(Error *error),
+ bool(CellularService::*set)(const string &value, Error *error)) {
+ mutable_store()->RegisterDerivedString(
+ name,
+ StringAccessor(
+ new CustomAccessor<CellularService, string>(this, get, set)));
+}
+
+void CellularService::HelpRegisterDerivedStringmap(
+ const string &name,
+ Stringmap(CellularService::*get)(Error *error),
+ bool(CellularService::*set)(
+ const Stringmap &value, Error *error)) {
+ mutable_store()->RegisterDerivedStringmap(
+ name,
+ StringmapAccessor(
+ new CustomAccessor<CellularService, Stringmap>(this, get, set)));
+}
+
+void CellularService::HelpRegisterDerivedBool(
+ const string &name,
+ bool(CellularService::*get)(Error *error),
+ bool(CellularService::*set)(const bool&, Error *)) {
+ mutable_store()->RegisterDerivedBool(
+ name,
+ BoolAccessor(new CustomAccessor<CellularService, bool>(this, get, set)));
+}
+
+Stringmap *CellularService::GetUserSpecifiedApn() {
+ Stringmap::iterator it = apn_info_.find(kApnProperty);
+ if (it == apn_info_.end() || it->second.empty())
+ return nullptr;
+ return &apn_info_;
+}
+
+Stringmap *CellularService::GetLastGoodApn() {
+ Stringmap::iterator it = last_good_apn_info_.find(kApnProperty);
+ if (it == last_good_apn_info_.end() || it->second.empty())
+ return nullptr;
+ return &last_good_apn_info_;
+}
+
+string CellularService::CalculateActivationType(Error *error) {
+ return GetActivationTypeString();
+}
+
+Stringmap CellularService::GetApn(Error */*error*/) {
+ return apn_info_;
+}
+
+bool CellularService::SetApn(const Stringmap &value, Error *error) {
+ // Only copy in the fields we care about, and validate the contents.
+ // If the "apn" field is missing or empty, the APN is cleared.
+ string str;
+ Stringmap new_apn_info;
+ if (GetNonEmptyField(value, kApnProperty, &str)) {
+ new_apn_info[kApnProperty] = str;
+ if (GetNonEmptyField(value, kApnUsernameProperty, &str))
+ new_apn_info[kApnUsernameProperty] = str;
+ if (GetNonEmptyField(value, kApnPasswordProperty, &str))
+ new_apn_info[kApnPasswordProperty] = str;
+ }
+ if (apn_info_ == new_apn_info) {
+ return false;
+ }
+ apn_info_ = new_apn_info;
+ if (ContainsKey(apn_info_, kApnProperty)) {
+ // Clear the last good APN, otherwise the one the user just
+ // set won't be used, since LastGoodApn comes first in the
+ // search order when trying to connect. Only do this if a
+ // non-empty user APN has been supplied. If the user APN is
+ // being cleared, leave LastGoodApn alone.
+ ClearLastGoodApn();
+ }
+ adaptor()->EmitStringmapChanged(kCellularApnProperty, apn_info_);
+ return true;
+}
+
+void CellularService::SetLastGoodApn(const Stringmap &apn_info) {
+ last_good_apn_info_ = apn_info;
+ adaptor()->EmitStringmapChanged(kCellularLastGoodApnProperty,
+ last_good_apn_info_);
+}
+
+void CellularService::ClearLastGoodApn() {
+ last_good_apn_info_.clear();
+ adaptor()->EmitStringmapChanged(kCellularLastGoodApnProperty,
+ last_good_apn_info_);
+}
+
+void CellularService::OnAfterResume() {
+ Service::OnAfterResume();
+ resume_start_time_ = base::Time::Now();
+}
+
+void CellularService::InitOutOfCreditsDetection(
+ OutOfCreditsDetector::OOCType ooc_type) {
+ out_of_credits_detector_.reset(
+ OutOfCreditsDetector::CreateDetector(ooc_type,
+ dispatcher(),
+ manager(),
+ metrics(),
+ this));
+}
+
+bool CellularService::Load(StoreInterface *storage) {
+ // Load properties common to all Services.
+ if (!Service::Load(storage))
+ return false;
+
+ const string id = GetStorageIdentifier();
+ LoadApn(storage, id, kStorageAPN, &apn_info_);
+ LoadApn(storage, id, kStorageLastGoodAPN, &last_good_apn_info_);
+
+ const string old_username = ppp_username_;
+ const string old_password = ppp_password_;
+ storage->GetString(id, kStoragePPPUsername, &ppp_username_);
+ storage->GetString(id, kStoragePPPPassword, &ppp_password_);
+ if (IsFailed() && failure() == kFailurePPPAuth &&
+ (old_username != ppp_username_ || old_password != ppp_password_)) {
+ SetState(kStateIdle);
+ }
+ return true;
+}
+
+void CellularService::LoadApn(StoreInterface *storage,
+ const string &storage_group,
+ const string &keytag,
+ Stringmap *apn_info) {
+ if (!LoadApnField(storage, storage_group, keytag, kApnProperty, apn_info))
+ return;
+ LoadApnField(storage, storage_group, keytag, kApnUsernameProperty, apn_info);
+ LoadApnField(storage, storage_group, keytag, kApnPasswordProperty, apn_info);
+}
+
+bool CellularService::LoadApnField(StoreInterface *storage,
+ const string &storage_group,
+ const string &keytag,
+ const string &apntag,
+ Stringmap *apn_info) {
+ string value;
+ if (storage->GetString(storage_group, keytag + "." + apntag, &value) &&
+ !value.empty()) {
+ (*apn_info)[apntag] = value;
+ return true;
+ }
+ return false;
+}
+
+bool CellularService::Save(StoreInterface *storage) {
+ // Save properties common to all Services.
+ if (!Service::Save(storage))
+ return false;
+
+ const string id = GetStorageIdentifier();
+ SaveApn(storage, id, GetUserSpecifiedApn(), kStorageAPN);
+ SaveApn(storage, id, GetLastGoodApn(), kStorageLastGoodAPN);
+ SaveString(storage, id, kStoragePPPUsername, ppp_username_, false, true);
+ SaveString(storage, id, kStoragePPPPassword, ppp_password_, false, true);
+ return true;
+}
+
+void CellularService::SaveApn(StoreInterface *storage,
+ const string &storage_group,
+ const Stringmap *apn_info,
+ const string &keytag) {
+ SaveApnField(storage, storage_group, apn_info, keytag, kApnProperty);
+ SaveApnField(storage, storage_group, apn_info, keytag, kApnUsernameProperty);
+ SaveApnField(storage, storage_group, apn_info, keytag, kApnPasswordProperty);
+}
+
+void CellularService::SaveApnField(StoreInterface *storage,
+ const string &storage_group,
+ const Stringmap *apn_info,
+ const string &keytag,
+ const string &apntag) {
+ const string key = keytag + "." + apntag;
+ string str;
+ if (apn_info && GetNonEmptyField(*apn_info, apntag, &str))
+ storage->SetString(storage_group, key, str);
+ else
+ storage->DeleteKey(storage_group, key);
+}
+
+bool CellularService::IsOutOfCredits(Error */*error*/) {
+ return out_of_credits_detector_->out_of_credits();
+}
+
+void CellularService::set_out_of_credits_detector(
+ OutOfCreditsDetector *detector) {
+ out_of_credits_detector_.reset(detector);
+}
+
+void CellularService::SignalOutOfCreditsChanged(bool state) const {
+ adaptor()->EmitBoolChanged(kOutOfCreditsProperty, state);
+}
+
+void CellularService::AutoConnect() {
+ is_auto_connecting_ = true;
+ Service::AutoConnect();
+ is_auto_connecting_ = false;
+}
+
+void CellularService::Connect(Error *error, const char *reason) {
+ Service::Connect(error, reason);
+ cellular_->Connect(error);
+ if (error->IsFailure())
+ out_of_credits_detector_->ResetDetector();
+}
+
+void CellularService::Disconnect(Error *error, const char *reason) {
+ Service::Disconnect(error, reason);
+ cellular_->Disconnect(error, reason);
+}
+
+void CellularService::ActivateCellularModem(const string &carrier,
+ Error *error,
+ const ResultCallback &callback) {
+ cellular_->Activate(carrier, error, callback);
+}
+
+void CellularService::CompleteCellularActivation(Error *error) {
+ cellular_->CompleteActivation(error);
+}
+
+void CellularService::SetState(ConnectState new_state) {
+ out_of_credits_detector_->NotifyServiceStateChanged(state(), new_state);
+ Service::SetState(new_state);
+}
+
+void CellularService::SetStorageIdentifier(const string &identifier) {
+ SLOG(this, 3) << __func__ << ": " << identifier;
+ storage_identifier_ = identifier;
+ std::replace_if(storage_identifier_.begin(),
+ storage_identifier_.end(),
+ &Service::IllegalChar, '_');
+}
+
+string CellularService::GetStorageIdentifier() const {
+ return storage_identifier_;
+}
+
+string CellularService::GetDeviceRpcId(Error */*error*/) const {
+ return cellular_->GetRpcIdentifier();
+}
+
+void CellularService::SetActivationType(ActivationType type) {
+ if (type == activation_type_) {
+ return;
+ }
+ activation_type_ = type;
+ adaptor()->EmitStringChanged(kActivationTypeProperty,
+ GetActivationTypeString());
+}
+
+string CellularService::GetActivationTypeString() const {
+ switch (activation_type_) {
+ case kActivationTypeNonCellular:
+ return shill::kActivationTypeNonCellular;
+ case kActivationTypeOMADM:
+ return shill::kActivationTypeOMADM;
+ case kActivationTypeOTA:
+ return shill::kActivationTypeOTA;
+ case kActivationTypeOTASP:
+ return shill::kActivationTypeOTASP;
+ case kActivationTypeUnknown:
+ return "";
+ default:
+ NOTREACHED();
+ return ""; // Make compiler happy.
+ }
+}
+
+void CellularService::SetActivationState(const string &state) {
+ if (state == activation_state_) {
+ return;
+ }
+ activation_state_ = state;
+ adaptor()->EmitStringChanged(kActivationStateProperty, state);
+ SetConnectableFull(state != kActivationStateNotActivated);
+}
+
+void CellularService::SetOLP(const string &url,
+ const string &method,
+ const string &post_data) {
+ Stringmap olp;
+ olp[kPaymentPortalURL] = url;
+ olp[kPaymentPortalMethod] = method;
+ olp[kPaymentPortalPostData] = post_data;
+
+ if (olp_ == olp) {
+ return;
+ }
+ olp_ = olp;
+ adaptor()->EmitStringmapChanged(kPaymentPortalProperty, olp);
+}
+
+void CellularService::SetUsageURL(const string &url) {
+ if (url == usage_url_) {
+ return;
+ }
+ usage_url_ = url;
+ adaptor()->EmitStringChanged(kUsageURLProperty, url);
+}
+
+void CellularService::SetNetworkTechnology(const string &technology) {
+ if (technology == network_technology_) {
+ return;
+ }
+ network_technology_ = technology;
+ adaptor()->EmitStringChanged(kNetworkTechnologyProperty,
+ technology);
+}
+
+void CellularService::SetRoamingState(const string &state) {
+ if (state == roaming_state_) {
+ return;
+ }
+ roaming_state_ = state;
+ adaptor()->EmitStringChanged(kRoamingStateProperty, state);
+}
+
+void CellularService::set_serving_operator(const Stringmap &serving_operator) {
+ if (serving_operator_ == serving_operator)
+ return;
+
+ serving_operator_ = serving_operator;
+ adaptor()->EmitStringmapChanged(kServingOperatorProperty, serving_operator_);
+}
+
+} // namespace shill
diff --git a/cellular/cellular_service.h b/cellular/cellular_service.h
new file mode 100644
index 0000000..1e2a5a0
--- /dev/null
+++ b/cellular/cellular_service.h
@@ -0,0 +1,233 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_CELLULAR_SERVICE_H_
+#define SHILL_CELLULAR_CELLULAR_SERVICE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/out_of_credits_detector.h"
+#include "shill/refptr_types.h"
+#include "shill/service.h"
+
+namespace shill {
+
+class ControlInterface;
+class Error;
+class EventDispatcher;
+class Manager;
+class OutOfCreditsDetector;
+
+class CellularService : public Service {
+ public:
+ enum ActivationType {
+ kActivationTypeNonCellular, // For future use
+ kActivationTypeOMADM, // For future use
+ kActivationTypeOTA,
+ kActivationTypeOTASP,
+ kActivationTypeUnknown
+ };
+
+ CellularService(ModemInfo *modem_info,
+ const CellularRefPtr &device);
+ ~CellularService() override;
+
+ // Inherited from Service.
+ virtual void AutoConnect();
+ virtual void Connect(Error *error, const char *reason);
+ virtual void Disconnect(Error *error, const char *reason);
+ virtual void ActivateCellularModem(const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback);
+ virtual void CompleteCellularActivation(Error *error);
+ virtual void SetState(ConnectState new_state);
+
+ virtual std::string GetStorageIdentifier() const;
+ void SetStorageIdentifier(const std::string &identifier);
+
+ const CellularRefPtr &cellular() const { return cellular_; }
+
+ void SetActivationType(ActivationType type);
+ std::string GetActivationTypeString() const;
+
+ virtual void SetActivationState(const std::string &state);
+ virtual const std::string &activation_state() const {
+ return activation_state_;
+ }
+
+ void SetOLP(const std::string &url,
+ const std::string &method,
+ const std::string &post_data);
+ const Stringmap &olp() const { return olp_; }
+
+ void SetUsageURL(const std::string &url);
+ const std::string &usage_url() const { return usage_url_; }
+
+ void set_serving_operator(const Stringmap &serving_operator);
+ const Stringmap &serving_operator() const { return serving_operator_; }
+
+ // Sets network technology to |technology| and broadcasts the property change.
+ void SetNetworkTechnology(const std::string &technology);
+ const std::string &network_technology() const { return network_technology_; }
+
+ // Sets roaming state to |state| and broadcasts the property change.
+ void SetRoamingState(const std::string &state);
+ const std::string &roaming_state() const { return roaming_state_; }
+
+ bool is_auto_connecting() const {
+ return is_auto_connecting_;
+ }
+
+ const std::string &ppp_username() const { return ppp_username_; }
+ const std::string &ppp_password() const { return ppp_password_; }
+
+ virtual const base::Time &resume_start_time() const {
+ return resume_start_time_;
+ }
+
+ OutOfCreditsDetector *out_of_credits_detector() {
+ return out_of_credits_detector_.get();
+ }
+ void SignalOutOfCreditsChanged(bool state) const;
+
+ // Overrides Load and Save from parent Service class. We will call
+ // the parent method.
+ virtual bool Load(StoreInterface *storage);
+ virtual bool Save(StoreInterface *storage);
+
+ Stringmap *GetUserSpecifiedApn();
+ Stringmap *GetLastGoodApn();
+ virtual void SetLastGoodApn(const Stringmap &apn_info);
+ virtual void ClearLastGoodApn();
+
+ virtual void OnAfterResume();
+
+ // Initialize out-of-credits detection.
+ void InitOutOfCreditsDetection(OutOfCreditsDetector::OOCType ooc_type);
+
+ protected:
+ // Overrides IsAutoConnectable from parent Service class.
+ virtual bool IsAutoConnectable(const char **reason) const;
+
+ private:
+ friend class CellularCapabilityUniversalTest;
+ friend class CellularServiceTest;
+ FRIEND_TEST(CellularCapabilityGSMTest, SetupApnTryList);
+ FRIEND_TEST(CellularCapabilityTest, TryApns);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest,
+ UpdatePendingActivationState);
+ FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateServiceName);
+ FRIEND_TEST(CellularTest, Connect);
+ FRIEND_TEST(CellularTest, GetLogin); // ppp_username_, ppp_password_
+ FRIEND_TEST(CellularTest, OnConnectionHealthCheckerResult);
+ FRIEND_TEST(CellularServiceTest, SetApn);
+ FRIEND_TEST(CellularServiceTest, ClearApn);
+ FRIEND_TEST(CellularServiceTest, LastGoodApn);
+ FRIEND_TEST(CellularServiceTest, LoadResetsPPPAuthFailure);
+ FRIEND_TEST(CellularServiceTest, IsAutoConnectable);
+ FRIEND_TEST(CellularServiceTest, OutOfCreditsDetected);
+ FRIEND_TEST(CellularServiceTest,
+ OutOfCreditsDetectionNotSkippedAfterSlowResume);
+ FRIEND_TEST(CellularServiceTest, OutOfCreditsDetectionSkippedAfterResume);
+ FRIEND_TEST(CellularServiceTest,
+ OutOfCreditsDetectionSkippedAlreadyOutOfCredits);
+ FRIEND_TEST(CellularServiceTest,
+ OutOfCreditsDetectionSkippedExplicitDisconnect);
+ FRIEND_TEST(CellularServiceTest, OutOfCreditsNotDetectedConnectionNotDropped);
+ FRIEND_TEST(CellularServiceTest, OutOfCreditsNotDetectedIntermittentNetwork);
+ FRIEND_TEST(CellularServiceTest, OutOfCreditsNotEnforced);
+ FRIEND_TEST(CellularServiceTest, CustomSetterNoopChange);
+
+ static const char kAutoConnActivating[];
+ static const char kAutoConnBadPPPCredentials[];
+ static const char kAutoConnDeviceDisabled[];
+ static const char kAutoConnOutOfCredits[];
+ static const char kAutoConnOutOfCreditsDetectionInProgress[];
+ static const char kStoragePPPUsername[];
+ static const char kStoragePPPPassword[];
+
+ void HelpRegisterDerivedString(
+ const std::string &name,
+ std::string(CellularService::*get)(Error *error),
+ bool(CellularService::*set)(const std::string &value, Error *error));
+ void HelpRegisterDerivedStringmap(
+ const std::string &name,
+ Stringmap(CellularService::*get)(Error *error),
+ bool(CellularService::*set)(const Stringmap &value, Error *error));
+ void HelpRegisterDerivedBool(
+ const std::string &name,
+ bool(CellularService::*get)(Error *error),
+ bool(CellularService::*set)(const bool&, Error *));
+
+ virtual std::string GetDeviceRpcId(Error *error) const;
+
+ std::string CalculateActivationType(Error *error);
+
+ Stringmap GetApn(Error *error);
+ bool SetApn(const Stringmap &value, Error *error);
+ static void SaveApn(StoreInterface *storage,
+ const std::string &storage_group,
+ const Stringmap *apn_info,
+ const std::string &keytag);
+ static void SaveApnField(StoreInterface *storage,
+ const std::string &storage_group,
+ const Stringmap *apn_info,
+ const std::string &keytag,
+ const std::string &apntag);
+ static void LoadApn(StoreInterface *storage,
+ const std::string &storage_group,
+ const std::string &keytag,
+ Stringmap *apn_info);
+ static bool LoadApnField(StoreInterface *storage,
+ const std::string &storage_group,
+ const std::string &keytag,
+ const std::string &apntag,
+ Stringmap *apn_info);
+ bool IsOutOfCredits(Error */*error*/);
+
+ // For unit test.
+ void set_out_of_credits_detector(OutOfCreditsDetector *detector);
+
+ base::WeakPtrFactory<CellularService> weak_ptr_factory_;
+
+ // Properties
+ ActivationType activation_type_;
+ std::string activation_state_;
+ Stringmap serving_operator_;
+ std::string network_technology_;
+ std::string roaming_state_;
+ Stringmap olp_;
+ std::string usage_url_;
+ Stringmap apn_info_;
+ Stringmap last_good_apn_info_;
+ std::string ppp_username_;
+ std::string ppp_password_;
+
+ std::string storage_identifier_;
+
+ CellularRefPtr cellular_;
+
+ // Flag indicating that a connect request is an auto-connect request.
+ // Note: Since Connect() is asynchronous, this flag is only set during the
+ // call to Connect(). It does not remain set while the async request is
+ // in flight.
+ bool is_auto_connecting_;
+ // Time when the last resume occurred.
+ base::Time resume_start_time_;
+ // Out-of-credits detector.
+ std::unique_ptr<OutOfCreditsDetector> out_of_credits_detector_;
+
+ DISALLOW_COPY_AND_ASSIGN(CellularService);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_CELLULAR_SERVICE_H_
diff --git a/cellular/cellular_service_unittest.cc b/cellular/cellular_service_unittest.cc
new file mode 100644
index 0000000..da40905
--- /dev/null
+++ b/cellular/cellular_service_unittest.cc
@@ -0,0 +1,525 @@
+// Copyright (c) 2012 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/cellular_service.h"
+
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+#include <mm/mm-modem.h>
+
+#include "shill/cellular/cellular_capability.h"
+#include "shill/cellular/cellular_capability_cdma.h"
+#include "shill/cellular/mock_cellular.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/cellular/mock_out_of_credits_detector.h"
+#include "shill/mock_adaptors.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_metrics.h"
+#include "shill/mock_profile.h"
+#include "shill/mock_store.h"
+#include "shill/nice_mock_control.h"
+#include "shill/proxy_factory.h"
+#include "shill/service_property_change_test.h"
+
+using std::string;
+using testing::_;
+using testing::InSequence;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::SetArgumentPointee;
+
+namespace shill {
+
+class CellularServiceTest : public testing::Test {
+ public:
+ CellularServiceTest()
+ : modem_info_(nullptr, &dispatcher_, nullptr, nullptr, nullptr),
+ device_(new MockCellular(&modem_info_,
+ "usb0",
+ kAddress,
+ 3,
+ Cellular::kTypeCDMA,
+ "",
+ "",
+ "",
+ ProxyFactory::GetInstance())),
+ service_(new CellularService(&modem_info_, device_)),
+ adaptor_(nullptr) {}
+
+ virtual ~CellularServiceTest() {
+ adaptor_ = nullptr;
+ }
+
+ virtual void SetUp() {
+ adaptor_ =
+ dynamic_cast<ServiceMockAdaptor *>(service_->adaptor());
+ out_of_credits_detector_ =
+ new MockOutOfCreditsDetector(nullptr, nullptr, nullptr, service_);
+ // Passes ownership.
+ service_->set_out_of_credits_detector(out_of_credits_detector_);
+ }
+
+ CellularCapabilityCDMA *GetCapabilityCDMA() {
+ return dynamic_cast<CellularCapabilityCDMA *>(device_->capability_.get());
+ }
+
+ protected:
+ static const char kAddress[];
+
+ string GetFriendlyName() const { return service_->friendly_name(); }
+
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ scoped_refptr<MockCellular> device_;
+ CellularServiceRefPtr service_;
+ ServiceMockAdaptor *adaptor_; // Owned by |service_|.
+ MockOutOfCreditsDetector *out_of_credits_detector_; // Owned by |service_|.
+};
+
+const char CellularServiceTest::kAddress[] = "000102030405";
+
+TEST_F(CellularServiceTest, Constructor) {
+ EXPECT_TRUE(service_->connectable());
+}
+
+TEST_F(CellularServiceTest, SetActivationState) {
+ {
+ InSequence call_sequence;
+ EXPECT_CALL(*adaptor_, EmitStringChanged(
+ kActivationStateProperty,
+ kActivationStateNotActivated));
+ EXPECT_CALL(*adaptor_, EmitBoolChanged(
+ kConnectableProperty, false));
+ EXPECT_CALL(*adaptor_, EmitStringChanged(
+ kActivationStateProperty,
+ kActivationStateActivating));
+ EXPECT_CALL(*adaptor_, EmitBoolChanged(
+ kConnectableProperty, true));
+ EXPECT_CALL(*adaptor_, EmitStringChanged(
+ kActivationStateProperty,
+ kActivationStatePartiallyActivated));
+ EXPECT_CALL(*adaptor_, EmitStringChanged(
+ kActivationStateProperty,
+ kActivationStateActivated));
+ EXPECT_CALL(*adaptor_, EmitStringChanged(
+ kActivationStateProperty,
+ kActivationStateNotActivated));
+ EXPECT_CALL(*adaptor_, EmitBoolChanged(
+ kConnectableProperty, false));
+ }
+ EXPECT_CALL(*modem_info_.mock_manager(), HasService(_))
+ .WillRepeatedly(Return(false));
+
+ EXPECT_TRUE(service_->activation_state().empty());
+ EXPECT_TRUE(service_->connectable());
+
+ service_->SetActivationState(kActivationStateNotActivated);
+ EXPECT_EQ(kActivationStateNotActivated, service_->activation_state());
+ EXPECT_FALSE(service_->connectable());
+
+ service_->SetActivationState(kActivationStateActivating);
+ EXPECT_EQ(kActivationStateActivating, service_->activation_state());
+ EXPECT_TRUE(service_->connectable());
+
+ service_->SetActivationState(kActivationStatePartiallyActivated);
+ EXPECT_EQ(kActivationStatePartiallyActivated, service_->activation_state());
+ EXPECT_TRUE(service_->connectable());
+
+ service_->SetActivationState(kActivationStateActivated);
+ EXPECT_EQ(kActivationStateActivated, service_->activation_state());
+ EXPECT_TRUE(service_->connectable());
+
+ service_->SetActivationState(kActivationStateNotActivated);
+ EXPECT_EQ(kActivationStateNotActivated, service_->activation_state());
+ EXPECT_FALSE(service_->connectable());
+}
+
+TEST_F(CellularServiceTest, SetNetworkTechnology) {
+ EXPECT_CALL(*adaptor_, EmitStringChanged(kNetworkTechnologyProperty,
+ kNetworkTechnologyUmts));
+ EXPECT_TRUE(service_->network_technology().empty());
+ service_->SetNetworkTechnology(kNetworkTechnologyUmts);
+ EXPECT_EQ(kNetworkTechnologyUmts, service_->network_technology());
+ service_->SetNetworkTechnology(kNetworkTechnologyUmts);
+}
+
+TEST_F(CellularServiceTest, SetRoamingState) {
+ EXPECT_CALL(*adaptor_, EmitStringChanged(kRoamingStateProperty,
+ kRoamingStateHome));
+ EXPECT_TRUE(service_->roaming_state().empty());
+ service_->SetRoamingState(kRoamingStateHome);
+ EXPECT_EQ(kRoamingStateHome, service_->roaming_state());
+ service_->SetRoamingState(kRoamingStateHome);
+}
+
+TEST_F(CellularServiceTest, SetStorageIdentifier) {
+ EXPECT_EQ(string(kTypeCellular) + "_" +
+ kAddress + "_" + GetFriendlyName(),
+ service_->GetStorageIdentifier());
+ service_->SetStorageIdentifier("a b c");
+ EXPECT_EQ("a_b_c", service_->GetStorageIdentifier());
+}
+
+TEST_F(CellularServiceTest, SetServingOperator) {
+ static const char kCode[] = "123456";
+ static const char kName[] = "Some Cellular Operator";
+ Stringmap test_operator;
+ service_->set_serving_operator(test_operator);
+ test_operator[kOperatorCodeKey] = kCode;
+ test_operator[kOperatorNameKey] = kName;
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kServingOperatorProperty, _));
+ service_->set_serving_operator(test_operator);
+ const Stringmap &serving_operator = service_->serving_operator();
+ ASSERT_NE(serving_operator.end(), serving_operator.find(kOperatorCodeKey));
+ ASSERT_NE(serving_operator.end(), serving_operator.find(kOperatorNameKey));
+ EXPECT_EQ(kCode, serving_operator.find(kOperatorCodeKey)->second);
+ EXPECT_EQ(kName, serving_operator.find(kOperatorNameKey)->second);
+ Mock::VerifyAndClearExpectations(adaptor_);
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kServingOperatorProperty, _)).Times(0);
+ service_->set_serving_operator(serving_operator);
+}
+
+TEST_F(CellularServiceTest, SetOLP) {
+ const char kMethod[] = "GET";
+ const char kURL[] = "payment.url";
+ const char kPostData[] = "post_man";
+ Stringmap olp;
+
+ service_->SetOLP("", "", "");
+ olp = service_->olp(); // Copy to simplify assertions below.
+ EXPECT_EQ("", olp[kPaymentPortalURL]);
+ EXPECT_EQ("", olp[kPaymentPortalMethod]);
+ EXPECT_EQ("", olp[kPaymentPortalPostData]);
+
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kPaymentPortalProperty, _));
+ service_->SetOLP(kURL, kMethod, kPostData);
+ olp = service_->olp(); // Copy to simplify assertions below.
+ EXPECT_EQ(kURL, olp[kPaymentPortalURL]);
+ EXPECT_EQ(kMethod, olp[kPaymentPortalMethod]);
+ EXPECT_EQ(kPostData, olp[kPaymentPortalPostData]);
+}
+
+TEST_F(CellularServiceTest, SetUsageURL) {
+ static const char kUsageURL[] = "usage.url";
+ EXPECT_CALL(*adaptor_, EmitStringChanged(kUsageURLProperty,
+ kUsageURL));
+ EXPECT_TRUE(service_->usage_url().empty());
+ service_->SetUsageURL(kUsageURL);
+ EXPECT_EQ(kUsageURL, service_->usage_url());
+ service_->SetUsageURL(kUsageURL);
+}
+
+TEST_F(CellularServiceTest, SetApn) {
+ static const char kApn[] = "TheAPN";
+ static const char kUsername[] = "commander.data";
+ ProfileRefPtr profile(new NiceMock<MockProfile>(
+ modem_info_.control_interface(), modem_info_.metrics(),
+ modem_info_.manager()));
+ service_->set_profile(profile);
+ Error error;
+ Stringmap testapn;
+ testapn[kApnProperty] = kApn;
+ testapn[kApnUsernameProperty] = kUsername;
+ {
+ InSequence seq;
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularLastGoodApnProperty,
+ _));
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularApnProperty, _));
+ }
+ service_->SetApn(testapn, &error);
+ EXPECT_TRUE(error.IsSuccess());
+ Stringmap resultapn = service_->GetApn(&error);
+ EXPECT_TRUE(error.IsSuccess());
+ EXPECT_EQ(2, resultapn.size());
+ Stringmap::const_iterator it = resultapn.find(kApnProperty);
+ EXPECT_TRUE(it != resultapn.end() && it->second == kApn);
+ it = resultapn.find(kApnUsernameProperty);
+ EXPECT_TRUE(it != resultapn.end() && it->second == kUsername);
+ EXPECT_NE(nullptr, service_->GetUserSpecifiedApn());
+}
+
+TEST_F(CellularServiceTest, ClearApn) {
+ static const char kApn[] = "TheAPN";
+ static const char kUsername[] = "commander.data";
+ ProfileRefPtr profile(new NiceMock<MockProfile>(
+ modem_info_.control_interface(), modem_info_.metrics(),
+ modem_info_.manager()));
+ service_->set_profile(profile);
+ Error error;
+ // Set up an APN to make sure that it later gets cleared.
+ Stringmap testapn;
+ testapn[kApnProperty] = kApn;
+ testapn[kApnUsernameProperty] = kUsername;
+ {
+ InSequence seq;
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularLastGoodApnProperty,
+ _));
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularApnProperty, _));
+ }
+ service_->SetApn(testapn, &error);
+ Stringmap resultapn = service_->GetApn(&error);
+ ASSERT_TRUE(error.IsSuccess());
+ ASSERT_EQ(2, service_->GetApn(&error).size());
+
+ Stringmap emptyapn;
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularLastGoodApnProperty,
+ _)).Times(0);
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularApnProperty, _)).Times(1);
+ service_->SetApn(emptyapn, &error);
+ EXPECT_TRUE(error.IsSuccess());
+ resultapn = service_->GetApn(&error);
+ EXPECT_TRUE(resultapn.empty());
+ EXPECT_EQ(nullptr, service_->GetUserSpecifiedApn());;
+}
+
+TEST_F(CellularServiceTest, LastGoodApn) {
+ static const char kApn[] = "TheAPN";
+ static const char kUsername[] = "commander.data";
+ ProfileRefPtr profile(new NiceMock<MockProfile>(
+ modem_info_.control_interface(), modem_info_.metrics(),
+ modem_info_.manager()));
+ service_->set_profile(profile);
+ Stringmap testapn;
+ testapn[kApnProperty] = kApn;
+ testapn[kApnUsernameProperty] = kUsername;
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularLastGoodApnProperty, _));
+ service_->SetLastGoodApn(testapn);
+ Stringmap *resultapn = service_->GetLastGoodApn();
+ EXPECT_NE(nullptr, resultapn);
+ EXPECT_EQ(2, resultapn->size());
+ Stringmap::const_iterator it = resultapn->find(kApnProperty);
+ EXPECT_TRUE(it != resultapn->end() && it->second == kApn);
+ it = resultapn->find(kApnUsernameProperty);
+ EXPECT_TRUE(it != resultapn->end() && it->second == kUsername);
+ // Now set the user-specified APN, and check that LastGoodApn got
+ // cleared.
+ Stringmap userapn;
+ userapn[kApnProperty] = kApn;
+ userapn[kApnUsernameProperty] = kUsername;
+ {
+ InSequence seq;
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularLastGoodApnProperty,
+ _));
+ EXPECT_CALL(*adaptor_,
+ EmitStringmapChanged(kCellularApnProperty, _));
+ }
+ Error error;
+ service_->SetApn(userapn, &error);
+ EXPECT_EQ(nullptr, service_->GetLastGoodApn());;
+}
+
+TEST_F(CellularServiceTest, IsAutoConnectable) {
+ const char *reason = nullptr;
+
+ ON_CALL(*out_of_credits_detector_, IsDetecting())
+ .WillByDefault(Return(false));
+
+ // Auto-connect should be suppressed if the device is not running.
+ device_->running_ = false;
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+ EXPECT_STREQ(CellularService::kAutoConnDeviceDisabled, reason);
+
+ device_->running_ = true;
+
+ // If we're waiting on a disconnect before an activation, don't auto-connect.
+ GetCapabilityCDMA()->activation_starting_ = true;
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+
+ // If we're waiting on an activation, also don't auto-connect.
+ GetCapabilityCDMA()->activation_starting_ = false;
+ GetCapabilityCDMA()->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+
+ GetCapabilityCDMA()->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
+
+ // Auto-connect should be suppressed if the we're undergoing an
+ // out-of-credits detection.
+ EXPECT_CALL(*out_of_credits_detector_, IsDetecting())
+ .WillOnce(Return(true));
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+ EXPECT_STREQ(CellularService::kAutoConnOutOfCreditsDetectionInProgress,
+ reason);
+ Mock::VerifyAndClearExpectations(out_of_credits_detector_);
+
+ // Auto-connect should be suppressed if we're out of credits.
+ EXPECT_CALL(*out_of_credits_detector_, IsDetecting())
+ .WillOnce(Return(false));
+ EXPECT_CALL(*out_of_credits_detector_, out_of_credits())
+ .WillOnce(Return(true));
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+ EXPECT_STREQ(CellularService::kAutoConnOutOfCredits, reason);
+ Mock::VerifyAndClearExpectations(out_of_credits_detector_);
+
+ EXPECT_CALL(*out_of_credits_detector_, out_of_credits())
+ .WillRepeatedly(Return(false));
+
+ // But other activation states are fine.
+ GetCapabilityCDMA()->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
+ EXPECT_TRUE(service_->IsAutoConnectable(&reason));
+ GetCapabilityCDMA()->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+ EXPECT_TRUE(service_->IsAutoConnectable(&reason));
+ GetCapabilityCDMA()->activation_state_ =
+ MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED;
+ EXPECT_TRUE(service_->IsAutoConnectable(&reason));
+
+ // A PPP authentication failure means the Service is not auto-connectable.
+ service_->SetFailure(Service::kFailurePPPAuth);
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+
+ // Reset failure state, to make the Service auto-connectable again.
+ service_->SetState(Service::kStateIdle);
+ EXPECT_TRUE(service_->IsAutoConnectable(&reason));
+
+ // The following test cases are copied from ServiceTest.IsAutoConnectable
+
+ service_->SetConnectable(true);
+ EXPECT_TRUE(service_->IsAutoConnectable(&reason));
+
+ // We should not auto-connect to a Service that a user has
+ // deliberately disconnected.
+ Error error;
+ service_->UserInitiatedDisconnect(&error);
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+ EXPECT_STREQ(Service::kAutoConnExplicitDisconnect, reason);
+
+ // But if the Service is reloaded, it is eligible for auto-connect
+ // again.
+ NiceMock<MockStore> storage;
+ EXPECT_CALL(storage, ContainsGroup(service_->GetStorageIdentifier()))
+ .WillOnce(Return(true));
+ EXPECT_TRUE(service_->Load(&storage));
+ EXPECT_TRUE(service_->IsAutoConnectable(&reason));
+
+ // A non-user initiated Disconnect doesn't change anything.
+ service_->Disconnect(&error, "in test");
+ EXPECT_TRUE(service_->IsAutoConnectable(&reason));
+
+ // A resume also re-enables auto-connect.
+ service_->UserInitiatedDisconnect(&error);
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+ service_->OnAfterResume();
+ EXPECT_TRUE(service_->IsAutoConnectable(&reason));
+
+ service_->SetState(Service::kStateConnected);
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+ EXPECT_STREQ(Service::kAutoConnConnected, reason);
+
+ service_->SetState(Service::kStateAssociating);
+ EXPECT_FALSE(service_->IsAutoConnectable(&reason));
+ EXPECT_STREQ(Service::kAutoConnConnecting, reason);
+}
+
+TEST_F(CellularServiceTest, LoadResetsPPPAuthFailure) {
+ NiceMock<MockStore> storage;
+ EXPECT_CALL(storage, ContainsGroup(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(storage, GetString(_, _, _)).WillRepeatedly(Return(true));
+
+ const string kDefaultUser;
+ const string kDefaultPass;
+ const string kNewUser("new-username");
+ const string kNewPass("new-password");
+ for (const auto change_username : { false, true }) {
+ for (const auto change_password : { false, true }) {
+ service_->ppp_username_ = kDefaultUser;
+ service_->ppp_password_ = kDefaultPass;
+ service_->SetFailure(Service::kFailurePPPAuth);
+ EXPECT_TRUE(service_->IsFailed());
+ EXPECT_EQ(Service::kFailurePPPAuth, service_->failure());
+ if (change_username) {
+ EXPECT_CALL(storage,
+ GetString(_, CellularService::kStoragePPPUsername, _))
+ .WillOnce(DoAll(SetArgumentPointee<2>(kNewUser), Return(true)))
+ .RetiresOnSaturation();
+ }
+ if (change_password) {
+ EXPECT_CALL(storage,
+ GetString(_, CellularService::kStoragePPPPassword, _))
+ .WillOnce(DoAll(SetArgumentPointee<2>(kNewPass), Return(true)))
+ .RetiresOnSaturation();
+ }
+ EXPECT_TRUE(service_->Load(&storage));
+ if (change_username || change_password) {
+ EXPECT_NE(Service::kFailurePPPAuth, service_->failure());
+ } else {
+ EXPECT_EQ(Service::kFailurePPPAuth, service_->failure());
+ }
+ }
+ }
+}
+
+// Some of these tests duplicate signals tested above. However, it's
+// convenient to have all the property change notifications documented
+// (and tested) in one place.
+TEST_F(CellularServiceTest, PropertyChanges) {
+ TestCommonPropertyChanges(service_, adaptor_);
+ TestAutoConnectPropertyChange(service_, adaptor_);
+
+ EXPECT_CALL(*adaptor_,
+ EmitStringChanged(kActivationTypeProperty, _));
+ service_->SetActivationType(CellularService::kActivationTypeOTA);
+ Mock::VerifyAndClearExpectations(adaptor_);
+
+ EXPECT_NE(kActivationStateNotActivated, service_->activation_state());
+ EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationStateProperty, _));
+ service_->SetActivationState(kActivationStateNotActivated);
+ Mock::VerifyAndClearExpectations(adaptor_);
+
+ string network_technology = service_->network_technology();
+ EXPECT_CALL(*adaptor_, EmitStringChanged(kNetworkTechnologyProperty, _));
+ service_->SetNetworkTechnology(network_technology + "and some new stuff");
+ Mock::VerifyAndClearExpectations(adaptor_);
+
+ bool out_of_credits = true;
+ EXPECT_CALL(*adaptor_,
+ EmitBoolChanged(kOutOfCreditsProperty, out_of_credits));
+ service_->SignalOutOfCreditsChanged(out_of_credits);
+ Mock::VerifyAndClearExpectations(adaptor_);
+
+ string roaming_state = service_->roaming_state();
+ EXPECT_CALL(*adaptor_, EmitStringChanged(kRoamingStateProperty, _));
+ service_->SetRoamingState(roaming_state + "and some new stuff");
+ Mock::VerifyAndClearExpectations(adaptor_);
+}
+
+// Custom property setters should return false, and make no changes, if
+// the new value is the same as the old value.
+TEST_F(CellularServiceTest, CustomSetterNoopChange) {
+ // Test that we didn't break any setters provided by the base class.
+ TestCustomSetterNoopChange(service_, modem_info_.mock_manager());
+
+ // Test the new setter we added.
+ // First set up our environment...
+ static const char kApn[] = "TheAPN";
+ static const char kUsername[] = "commander.data";
+ Error error;
+ Stringmap testapn;
+ ProfileRefPtr profile(new NiceMock<MockProfile>(nullptr, nullptr, nullptr));
+ service_->set_profile(profile);
+ testapn[kApnProperty] = kApn;
+ testapn[kApnUsernameProperty] = kUsername;
+ // ... then set to a known value ...
+ EXPECT_TRUE(service_->SetApn(testapn, &error));
+ EXPECT_TRUE(error.IsSuccess());
+ // ... then set to same value.
+ EXPECT_FALSE(service_->SetApn(testapn, &error));
+ EXPECT_TRUE(error.IsSuccess());
+}
+
+} // namespace shill
diff --git a/cellular/cellular_unittest.cc b/cellular/cellular_unittest.cc
new file mode 100644
index 0000000..b8cbfaa
--- /dev/null
+++ b/cellular/cellular_unittest.cc
@@ -0,0 +1,2143 @@
+// Copyright (c) 2013 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/cellular.h"
+
+#include <sys/socket.h>
+#include <linux/if.h> // NOLINT - Needs typedefs from sys/socket.h.
+#include <linux/netlink.h>
+
+#include <base/bind.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/cellular/cellular_bearer.h"
+#include "shill/cellular/cellular_capability_cdma.h"
+#include "shill/cellular/cellular_capability_classic.h"
+#include "shill/cellular/cellular_capability_gsm.h"
+#include "shill/cellular/cellular_capability_universal.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/mock_cellular_service.h"
+#include "shill/cellular/mock_mm1_modem_modem3gpp_proxy.h"
+#include "shill/cellular/mock_mm1_modem_proxy.h"
+#include "shill/cellular/mock_mm1_modem_simple_proxy.h"
+#include "shill/cellular/mock_mobile_operator_info.h"
+#include "shill/cellular/mock_modem_cdma_proxy.h"
+#include "shill/cellular/mock_modem_gsm_card_proxy.h"
+#include "shill/cellular/mock_modem_gsm_network_proxy.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/cellular/mock_modem_proxy.h"
+#include "shill/cellular/mock_modem_simple_proxy.h"
+#include "shill/error.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_adaptors.h"
+#include "shill/mock_dbus_properties_proxy.h"
+#include "shill/mock_device_info.h"
+#include "shill/mock_dhcp_config.h"
+#include "shill/mock_dhcp_provider.h"
+#include "shill/mock_external_task.h"
+#include "shill/mock_ppp_device.h"
+#include "shill/mock_ppp_device_factory.h"
+#include "shill/net/mock_rtnl_handler.h"
+#include "shill/property_store_unittest.h"
+#include "shill/proxy_factory.h"
+#include "shill/rpc_task.h" // for RpcTaskDelegate
+#include "shill/testing.h"
+
+// mm/mm-modem.h must be included after cellular_capability_universal.h
+// in order to allow MM_MODEM_CDMA_* to be defined properly.
+#include <mm/mm-modem.h>
+
+using base::Bind;
+using base::Unretained;
+using std::map;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SaveArg;
+using testing::SetArgumentPointee;
+using testing::Unused;
+
+namespace shill {
+
+class CellularPropertyTest : public PropertyStoreTest {
+ public:
+ CellularPropertyTest()
+ : modem_info_(control_interface(),
+ dispatcher(),
+ metrics(),
+ manager(),
+ glib()),
+ device_(new Cellular(&modem_info_,
+ "usb0",
+ "00:01:02:03:04:05",
+ 3,
+ Cellular::kTypeCDMA,
+ "",
+ "",
+ "",
+ ProxyFactory::GetInstance())) {}
+ virtual ~CellularPropertyTest() {}
+
+ protected:
+ MockModemInfo modem_info_;
+ DeviceRefPtr device_;
+};
+
+TEST_F(CellularPropertyTest, Contains) {
+ EXPECT_TRUE(device_->store().Contains(kNameProperty));
+ EXPECT_FALSE(device_->store().Contains(""));
+}
+
+TEST_F(CellularPropertyTest, SetProperty) {
+ {
+ ::DBus::Error error;
+ ::DBus::Variant allow_roaming;
+ allow_roaming.writer().append_bool(true);
+ EXPECT_TRUE(DBusAdaptor::SetProperty(
+ device_->mutable_store(),
+ kCellularAllowRoamingProperty,
+ allow_roaming,
+ &error));
+ }
+ // Ensure that attempting to write a R/O property returns InvalidArgs error.
+ {
+ ::DBus::Error error;
+ EXPECT_FALSE(DBusAdaptor::SetProperty(device_->mutable_store(),
+ kAddressProperty,
+ PropertyStoreTest::kStringV,
+ &error));
+ ASSERT_TRUE(error.is_set()); // name() may be invalid otherwise
+ EXPECT_EQ(invalid_args(), error.name());
+ }
+ {
+ ::DBus::Error error;
+ EXPECT_FALSE(DBusAdaptor::SetProperty(device_->mutable_store(),
+ kCarrierProperty,
+ PropertyStoreTest::kStringV,
+ &error));
+ ASSERT_TRUE(error.is_set()); // name() may be invalid otherwise
+ EXPECT_EQ(invalid_args(), error.name());
+ }
+}
+
+class CellularTest : public testing::Test {
+ public:
+ CellularTest()
+ : kHomeProviderCode("10001"),
+ kHomeProviderCountry("us"),
+ kHomeProviderName("HomeProviderName"),
+ kServingOperatorCode("10002"),
+ kServingOperatorCountry("ca"),
+ kServingOperatorName("ServingOperatorName"),
+ modem_info_(nullptr, &dispatcher_, nullptr, nullptr, nullptr),
+ device_info_(modem_info_.control_interface(), &dispatcher_,
+ modem_info_.metrics(), modem_info_.manager()),
+ dhcp_config_(new MockDHCPConfig(modem_info_.control_interface(),
+ kTestDeviceName)),
+ create_gsm_card_proxy_from_factory_(false),
+ proxy_factory_(this),
+ mock_home_provider_info_(nullptr),
+ mock_serving_operator_info_(nullptr),
+ device_(new Cellular(&modem_info_,
+ kTestDeviceName,
+ kTestDeviceAddress,
+ 3,
+ Cellular::kTypeGSM,
+ kDBusOwner,
+ kDBusService,
+ kDBusPath,
+ &proxy_factory_)) {
+ PopulateProxies();
+ modem_info_.metrics()->RegisterDevice(device_->interface_index(),
+ Technology::kCellular);
+ }
+
+ virtual void SetUp() {
+ static_cast<Device *>(device_)->rtnl_handler_ = &rtnl_handler_;
+ device_->set_dhcp_provider(&dhcp_provider_);
+ EXPECT_CALL(*modem_info_.mock_manager(), device_info())
+ .WillRepeatedly(Return(&device_info_));
+ EXPECT_CALL(*modem_info_.mock_manager(), DeregisterService(_))
+ .Times(AnyNumber());
+ }
+
+ virtual void TearDown() {
+ device_->DestroyIPConfig();
+ device_->state_ = Cellular::kStateDisabled;
+ device_->capability_->ReleaseProxies();
+ device_->set_dhcp_provider(nullptr);
+ // Break cycle between Cellular and CellularService.
+ device_->service_ = nullptr;
+ device_->SelectService(nullptr);
+ }
+
+ void PopulateProxies() {
+ dbus_properties_proxy_.reset(new MockDBusPropertiesProxy());
+ proxy_.reset(new MockModemProxy());
+ simple_proxy_.reset(new MockModemSimpleProxy());
+ cdma_proxy_.reset(new MockModemCDMAProxy());
+ gsm_card_proxy_.reset(new MockModemGSMCardProxy());
+ gsm_network_proxy_.reset(new MockModemGSMNetworkProxy());
+ mm1_modem_3gpp_proxy_.reset(new mm1::MockModemModem3gppProxy());
+ mm1_proxy_.reset(new mm1::MockModemProxy());
+ mm1_simple_proxy_.reset(new mm1::MockModemSimpleProxy());
+ }
+
+ void SetMockMobileOperatorInfoObjects() {
+ mock_home_provider_info_ =
+ new MockMobileOperatorInfo(&dispatcher_, "HomeProvider");
+ // Takes ownership.
+ device_->set_home_provider_info(mock_home_provider_info_);
+
+ mock_serving_operator_info_ =
+ new MockMobileOperatorInfo(&dispatcher_, "ServingOperator");
+ // Takes ownership.
+ device_->set_serving_operator_info(mock_serving_operator_info_);
+ }
+
+ void InvokeEnable(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeEnableReturningWrongState(
+ bool enable, Error *error, const ResultCallback &callback, int timeout) {
+ callback.Run(Error(Error::kWrongState));
+ }
+ void InvokeGetSignalQuality(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout) {
+ callback.Run(kStrength, Error());
+ }
+ void InvokeGetModemStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout) {
+ DBusPropertiesMap props;
+ props["carrier"].writer().append_string(kTestCarrier);
+ props["unknown-property"].writer().append_string("irrelevant-value");
+ callback.Run(props, Error());
+ }
+ void InvokeGetModemInfo(Error *error, const ModemInfoCallback &callback,
+ int timeout) {
+ static const char kManufacturer[] = "Company";
+ static const char kModelID[] = "Gobi 2000";
+ static const char kHWRev[] = "A00B1234";
+ ModemHardwareInfo info;
+ info._1 = kManufacturer;
+ info._2 = kModelID;
+ info._3 = kHWRev;
+ callback.Run(info, Error());
+ }
+ void InvokeGetRegistrationState1X(Error *error,
+ const RegistrationStateCallback &callback,
+ int timeout) {
+ callback.Run(MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ Error());
+ }
+ void InvokeGetIMEI(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run(kIMEI, Error());
+ }
+ void InvokeGetIMSI(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run(kIMSI, Error());
+ }
+ void InvokeGetMSISDN(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run(kMSISDN, Error());
+ }
+ void InvokeGetSPN(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) {
+ callback.Run(kTestCarrierSPN, Error());
+ }
+ void InvokeGetRegistrationInfo(Error *error,
+ const RegistrationInfoCallback &callback,
+ int timeout) {
+ static const char kNetworkID[] = "22803";
+ callback.Run(MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING,
+ kNetworkID, kTestCarrier, Error());
+ }
+ void InvokeRegister(const string &network_id,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ callback.Run(Error());
+ }
+ void InvokeGetRegistrationState(Error *error,
+ const RegistrationStateCallback &callback,
+ int timeout) {
+ callback.Run(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+ Error());
+ }
+ void InvokeGetRegistrationStateUnregistered(
+ Error *error,
+ const RegistrationStateCallback &callback,
+ int timeout) {
+ callback.Run(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ Error());
+ }
+ void InvokeConnect(DBusPropertiesMap props, Error *error,
+ const ResultCallback &callback, int timeout) {
+ EXPECT_EQ(Service::kStateAssociating, device_->service_->state());
+ callback.Run(Error());
+ }
+ void InvokeConnectFail(DBusPropertiesMap props, Error *error,
+ const ResultCallback &callback, int timeout) {
+ EXPECT_EQ(Service::kStateAssociating, device_->service_->state());
+ callback.Run(Error(Error::kNotOnHomeNetwork));
+ }
+ void InvokeConnectFailNoService(DBusPropertiesMap props, Error *error,
+ const ResultCallback &callback, int timeout) {
+ device_->service_ = nullptr;
+ callback.Run(Error(Error::kNotOnHomeNetwork));
+ }
+ void InvokeConnectSuccessNoService(DBusPropertiesMap props, Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ device_->service_ = nullptr;
+ callback.Run(Error());
+ }
+ void InvokeDisconnect(Error *error, const ResultCallback &callback,
+ int timeout) {
+ if (!callback.is_null())
+ callback.Run(Error());
+ }
+ void InvokeDisconnectFail(Error *error, const ResultCallback &callback,
+ int timeout) {
+ error->Populate(Error::kOperationFailed);
+ if (!callback.is_null())
+ callback.Run(*error);
+ }
+ void InvokeDisconnectMM1(const ::DBus::Path &bearer, Error *error,
+ const ResultCallback &callback, int timeout) {
+ if (!callback.is_null())
+ callback.Run(Error());
+ }
+ void InvokeSetPowerState(const uint32_t &power_state,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ callback.Run(Error());
+ }
+ void ExpectCdmaStartModem(string network_technology) {
+ if (!device_->IsUnderlyingDeviceEnabled())
+ EXPECT_CALL(*proxy_,
+ Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+ EXPECT_CALL(*simple_proxy_,
+ GetModemStatus(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
+ EXPECT_CALL(*proxy_,
+ GetModemInfo(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetModemInfo));
+ if (network_technology == kNetworkTechnology1Xrtt)
+ EXPECT_CALL(*cdma_proxy_, GetRegistrationState(nullptr, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetRegistrationState1X));
+ else
+ EXPECT_CALL(*cdma_proxy_, GetRegistrationState(nullptr, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetRegistrationState));
+ EXPECT_CALL(*cdma_proxy_, GetSignalQuality(nullptr, _, _))
+ .Times(2)
+ .WillRepeatedly(Invoke(this, &CellularTest::InvokeGetSignalQuality));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ EXPECT_CALL(*modem_info_.mock_manager(), RegisterService(_));
+ }
+
+ void ExpectDisconnectCapabilityUniversal() {
+ SetCellularType(Cellular::kTypeUniversal);
+ device_->state_ = Cellular::kStateConnected;
+ EXPECT_CALL(*mm1_simple_proxy_, Disconnect(_, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeDisconnectMM1));
+ GetCapabilityUniversal()->modem_simple_proxy_.reset(
+ mm1_simple_proxy_.release());
+ }
+
+ void VerifyDisconnect() {
+ EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
+ }
+
+ void StartPPP(int pid) {
+ MockGLib &mock_glib(*dynamic_cast<MockGLib *>(modem_info_.glib()));
+ EXPECT_CALL(mock_glib, ChildWatchAdd(pid, _, _));
+ EXPECT_CALL(mock_glib, SpawnAsync(_, _, _, _, _, _, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<6>(pid), Return(true)));
+ device_->StartPPP("fake_serial_device");
+ EXPECT_FALSE(device_->ipconfig()); // No DHCP client.
+ EXPECT_FALSE(device_->selected_service());
+ EXPECT_FALSE(device_->is_ppp_authenticating_);
+ EXPECT_NE(nullptr, device_->ppp_task_);
+ Mock::VerifyAndClearExpectations(&mock_glib);
+ }
+
+ void FakeUpConnectedPPP() {
+ const char kInterfaceName[] = "fake-ppp-device";
+ const int kInterfaceIndex = -1;
+ auto mock_ppp_device = make_scoped_refptr(
+ new MockPPPDevice(modem_info_.control_interface(), nullptr, nullptr,
+ nullptr, kInterfaceName, kInterfaceIndex));
+ device_->ppp_device_ = mock_ppp_device;
+ device_->state_ = Cellular::kStateConnected;
+ }
+
+ void ExpectPPPStopped() {
+ auto mock_ppp_device =
+ dynamic_cast<MockPPPDevice *>(device_->ppp_device_.get());
+ EXPECT_CALL(*mock_ppp_device, DropConnection());
+ }
+
+ void VerifyPPPStopped() {
+ EXPECT_EQ(nullptr, device_->ppp_task_);
+ EXPECT_FALSE(device_->ppp_device_);
+ }
+
+ void SetCommonOnAfterResumeExpectations() {
+ EXPECT_CALL(*dbus_properties_proxy_, GetAll(_))
+ .WillRepeatedly(Return(DBusPropertiesMap()));
+ EXPECT_CALL(*mm1_proxy_, set_state_changed_callback(_)).Times(AnyNumber());
+ EXPECT_CALL(*modem_info_.mock_metrics(), NotifyDeviceScanStarted(_))
+ .Times(AnyNumber());
+ EXPECT_CALL(*modem_info_.mock_manager(), UpdateEnabledTechnologies())
+ .Times(AnyNumber());
+ EXPECT_CALL(*dynamic_cast<DeviceMockAdaptor *>(device_->adaptor()),
+ EmitBoolChanged(_, _)).Times(AnyNumber());
+ }
+
+ mm1::MockModemProxy *SetupOnAfterResume() {
+ SetCellularType(Cellular::kTypeUniversal);
+ SetCommonOnAfterResumeExpectations();
+ return mm1_proxy_.get(); // Before the capability snags it.
+ }
+
+ void VerifyOperatorMap(const Stringmap &operator_map,
+ const string &code,
+ const string &name,
+ const string &country) {
+ Stringmap::const_iterator it;
+ Stringmap::const_iterator endit = operator_map.end();
+
+ it = operator_map.find(kOperatorCodeKey);
+ if (code == "") {
+ EXPECT_EQ(endit, it);
+ } else {
+ ASSERT_NE(endit, it);
+ EXPECT_EQ(code, it->second);
+ }
+ it = operator_map.find(kOperatorNameKey);
+ if (name == "") {
+ EXPECT_EQ(endit, it);
+ } else {
+ ASSERT_NE(endit, it);
+ EXPECT_EQ(name, it->second);
+ }
+ it = operator_map.find(kOperatorCountryKey);
+ if (country == "") {
+ EXPECT_EQ(endit, it);
+ } else {
+ ASSERT_NE(endit, it);
+ EXPECT_EQ(country, it->second);
+ }
+ }
+
+ MOCK_METHOD1(TestCallback, void(const Error &error));
+
+ protected:
+ static const char kTestDeviceName[];
+ static const char kTestDeviceAddress[];
+ static const char kDBusOwner[];
+ static const char kDBusService[];
+ static const char kDBusPath[];
+ static const char kTestCarrier[];
+ static const char kTestCarrierSPN[];
+ static const char kMEID[];
+ static const char kIMEI[];
+ static const char kIMSI[];
+ static const char kMSISDN[];
+ static const char kTestMobileProviderDBPath[];
+ static const Stringmaps kTestNetworksGSM;
+ static const Stringmaps kTestNetworksCellular;
+ static const int kStrength;
+
+ // Must be std::string so that we can safely ReturnRef.
+ const string kHomeProviderCode;
+ const string kHomeProviderCountry;
+ const string kHomeProviderName;
+ const string kServingOperatorCode;
+ const string kServingOperatorCountry;
+ const string kServingOperatorName;
+
+ class TestProxyFactory : public ProxyFactory {
+ public:
+ explicit TestProxyFactory(CellularTest *test) : test_(test) {}
+
+ virtual DBusPropertiesProxyInterface *CreateDBusPropertiesProxy(
+ const std::string &path,
+ const std::string &service) {
+ CHECK(test_->dbus_properties_proxy_);
+ return test_->dbus_properties_proxy_.release();
+ }
+
+ virtual ModemProxyInterface *CreateModemProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ CHECK(test_->proxy_);
+ return test_->proxy_.release();
+ }
+
+ virtual ModemSimpleProxyInterface *CreateModemSimpleProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ CHECK(test_->simple_proxy_);
+ return test_->simple_proxy_.release();
+ }
+
+ virtual ModemCDMAProxyInterface *CreateModemCDMAProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ CHECK(test_->cdma_proxy_);
+ return test_->cdma_proxy_.release();
+ }
+
+ virtual ModemGSMCardProxyInterface *CreateModemGSMCardProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ // TODO(benchan): This code conditionally returns a nullptr to avoid
+ // CellularCapabilityGSM::InitProperties (and thus
+ // CellularCapabilityGSM::GetIMSI) from being called during the
+ // construction. Remove this workaround after refactoring the tests.
+ CHECK(!test_->create_gsm_card_proxy_from_factory_ ||
+ test_->gsm_card_proxy_);
+ return test_->create_gsm_card_proxy_from_factory_ ?
+ test_->gsm_card_proxy_.release() : nullptr;
+ }
+
+ virtual ModemGSMNetworkProxyInterface *CreateModemGSMNetworkProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ CHECK(test_->gsm_network_proxy_);
+ return test_->gsm_network_proxy_.release();
+ }
+
+ virtual mm1::ModemModem3gppProxyInterface *CreateMM1ModemModem3gppProxy(
+ const std::string &path,
+ const std::string &service) {
+ CHECK(test_->mm1_modem_3gpp_proxy_);
+ return test_->mm1_modem_3gpp_proxy_.release();
+ }
+
+ virtual mm1::ModemProxyInterface *CreateMM1ModemProxy(
+ const std::string &path,
+ const std::string &service) {
+ CHECK(test_->mm1_proxy_);
+ return test_->mm1_proxy_.release();
+ }
+
+ virtual mm1::ModemSimpleProxyInterface *CreateMM1ModemSimpleProxy(
+ const string &/*path*/,
+ const string &/*service*/) {
+ CHECK(test_->mm1_simple_proxy_);
+ return test_->mm1_simple_proxy_.release();
+ }
+
+ private:
+ CellularTest *test_;
+ };
+ void StartRTNLHandler();
+ void StopRTNLHandler();
+
+ void AllowCreateGSMCardProxyFromFactory() {
+ create_gsm_card_proxy_from_factory_ = true;
+ }
+
+ void SetCellularType(Cellular::Type type) {
+ device_->InitCapability(type);
+ }
+
+ CellularCapabilityClassic *GetCapabilityClassic() {
+ return dynamic_cast<CellularCapabilityClassic *>(
+ device_->capability_.get());
+ }
+
+ CellularCapabilityCDMA *GetCapabilityCDMA() {
+ return dynamic_cast<CellularCapabilityCDMA *>(device_->capability_.get());
+ }
+
+ CellularCapabilityGSM *GetCapabilityGSM() {
+ return dynamic_cast<CellularCapabilityGSM *>(device_->capability_.get());
+ }
+
+ CellularCapabilityUniversal *GetCapabilityUniversal() {
+ return dynamic_cast<CellularCapabilityUniversal *>(
+ device_->capability_.get());
+ }
+
+ // Different tests simulate a cellular service being set using a real /mock
+ // service.
+ CellularService *SetService() {
+ device_->service_ = new CellularService(&modem_info_, device_);
+ return device_->service_;
+ }
+ MockCellularService *SetMockService() {
+ device_->service_ = new MockCellularService(&modem_info_, device_);
+ return static_cast<MockCellularService *>(device_->service_.get());
+ }
+
+ void set_enabled_persistent(bool new_value) {
+ device_->enabled_persistent_ = new_value;
+ }
+
+ void SetCapabilityUniversalActiveBearer(unique_ptr<CellularBearer> bearer) {
+ SetCellularType(Cellular::kTypeUniversal);
+ CellularCapabilityUniversal *capability = GetCapabilityUniversal();
+ capability->active_bearer_ = std::move(bearer);
+ }
+
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ MockDeviceInfo device_info_;
+ NiceMock<MockRTNLHandler> rtnl_handler_;
+
+ MockDHCPProvider dhcp_provider_;
+ scoped_refptr<MockDHCPConfig> dhcp_config_;
+
+ bool create_gsm_card_proxy_from_factory_;
+ unique_ptr<MockDBusPropertiesProxy> dbus_properties_proxy_;
+ unique_ptr<MockModemProxy> proxy_;
+ unique_ptr<MockModemSimpleProxy> simple_proxy_;
+ unique_ptr<MockModemCDMAProxy> cdma_proxy_;
+ unique_ptr<MockModemGSMCardProxy> gsm_card_proxy_;
+ unique_ptr<MockModemGSMNetworkProxy> gsm_network_proxy_;
+ unique_ptr<mm1::MockModemModem3gppProxy> mm1_modem_3gpp_proxy_;
+ unique_ptr<mm1::MockModemProxy> mm1_proxy_;
+ unique_ptr<mm1::MockModemSimpleProxy> mm1_simple_proxy_;
+ TestProxyFactory proxy_factory_;
+ MockMobileOperatorInfo *mock_home_provider_info_;
+ MockMobileOperatorInfo *mock_serving_operator_info_;
+ CellularRefPtr device_;
+};
+
+const char CellularTest::kTestDeviceName[] = "usb0";
+const char CellularTest::kTestDeviceAddress[] = "00:01:02:03:04:05";
+const char CellularTest::kDBusOwner[] = ":1.19";
+const char CellularTest::kDBusService[] = "org.chromium.ModemManager";
+const char CellularTest::kDBusPath[] = "/org/chromium/ModemManager/Gobi/0";
+const char CellularTest::kTestCarrier[] = "The Cellular Carrier";
+const char CellularTest::kTestCarrierSPN[] = "Home Provider";
+const char CellularTest::kMEID[] = "01234567EF8901";
+const char CellularTest::kIMEI[] = "987654321098765";
+const char CellularTest::kIMSI[] = "123456789012345";
+const char CellularTest::kMSISDN[] = "12345678901";
+const char CellularTest::kTestMobileProviderDBPath[] =
+ "provider_db_unittest.bfd";
+const Stringmaps CellularTest::kTestNetworksGSM =
+ {{{CellularCapabilityGSM::kNetworkPropertyStatus, "1"},
+ {CellularCapabilityGSM::kNetworkPropertyID, "0000"},
+ {CellularCapabilityGSM::kNetworkPropertyLongName, "some_long_name"},
+ {CellularCapabilityGSM::kNetworkPropertyShortName, "short"}}};
+const Stringmaps CellularTest::kTestNetworksCellular =
+ {{{kStatusProperty, "available"},
+ {kNetworkIdProperty, "0000"},
+ {kLongNameProperty, "some_long_name"},
+ {kShortNameProperty, "short"}}};
+const int CellularTest::kStrength = 90;
+
+TEST_F(CellularTest, GetStateString) {
+ EXPECT_EQ("CellularStateDisabled",
+ Cellular::GetStateString(Cellular::kStateDisabled));
+ EXPECT_EQ("CellularStateEnabled",
+ Cellular::GetStateString(Cellular::kStateEnabled));
+ EXPECT_EQ("CellularStateRegistered",
+ Cellular::GetStateString(Cellular::kStateRegistered));
+ EXPECT_EQ("CellularStateConnected",
+ Cellular::GetStateString(Cellular::kStateConnected));
+ EXPECT_EQ("CellularStateLinked",
+ Cellular::GetStateString(Cellular::kStateLinked));
+}
+
+TEST_F(CellularTest, GetModemStateString) {
+ EXPECT_EQ("CellularModemStateFailed",
+ Cellular::GetModemStateString(Cellular::kModemStateFailed));
+ EXPECT_EQ("CellularModemStateUnknown",
+ Cellular::GetModemStateString(Cellular::kModemStateUnknown));
+ EXPECT_EQ("CellularModemStateInitializing",
+ Cellular::GetModemStateString(Cellular::kModemStateInitializing));
+ EXPECT_EQ("CellularModemStateLocked",
+ Cellular::GetModemStateString(Cellular::kModemStateLocked));
+ EXPECT_EQ("CellularModemStateDisabled",
+ Cellular::GetModemStateString(Cellular::kModemStateDisabled));
+ EXPECT_EQ("CellularModemStateDisabling",
+ Cellular::GetModemStateString(Cellular::kModemStateDisabling));
+ EXPECT_EQ("CellularModemStateEnabling",
+ Cellular::GetModemStateString(Cellular::kModemStateEnabling));
+ EXPECT_EQ("CellularModemStateEnabled",
+ Cellular::GetModemStateString(Cellular::kModemStateEnabled));
+ EXPECT_EQ("CellularModemStateSearching",
+ Cellular::GetModemStateString(Cellular::kModemStateSearching));
+ EXPECT_EQ("CellularModemStateRegistered",
+ Cellular::GetModemStateString(Cellular::kModemStateRegistered));
+ EXPECT_EQ("CellularModemStateDisconnecting",
+ Cellular::GetModemStateString(Cellular::kModemStateDisconnecting));
+ EXPECT_EQ("CellularModemStateConnecting",
+ Cellular::GetModemStateString(Cellular::kModemStateConnecting));
+ EXPECT_EQ("CellularModemStateConnected",
+ Cellular::GetModemStateString(Cellular::kModemStateConnected));
+}
+
+TEST_F(CellularTest, StartCDMARegister) {
+ SetCellularType(Cellular::kTypeCDMA);
+ ExpectCdmaStartModem(kNetworkTechnology1Xrtt);
+ EXPECT_CALL(*cdma_proxy_, MEID()).WillOnce(Return(kMEID));
+ Error error;
+ device_->Start(&error, Bind(&CellularTest::TestCallback, Unretained(this)));
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(kMEID, device_->meid());
+ EXPECT_EQ(kTestCarrier, device_->carrier());
+ EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
+ ASSERT_TRUE(device_->service_.get());
+ EXPECT_EQ(kNetworkTechnology1Xrtt, device_->service_->network_technology());
+ EXPECT_EQ(kStrength, device_->service_->strength());
+ EXPECT_EQ(kRoamingStateHome, device_->service_->roaming_state());
+}
+
+TEST_F(CellularTest, StartGSMRegister) {
+ SetMockMobileOperatorInfoObjects();
+ EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+ EXPECT_CALL(*gsm_card_proxy_,
+ GetIMEI(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetIMEI));
+ EXPECT_CALL(*gsm_card_proxy_,
+ GetIMSI(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetIMSI));
+ EXPECT_CALL(*gsm_card_proxy_,
+ GetSPN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetSPN));
+ EXPECT_CALL(*gsm_card_proxy_,
+ GetMSISDN(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetMSISDN));
+ EXPECT_CALL(*gsm_network_proxy_, AccessTechnology())
+ .WillOnce(Return(MM_MODEM_GSM_ACCESS_TECH_EDGE));
+ EXPECT_CALL(*gsm_card_proxy_, EnabledFacilityLocks())
+ .WillOnce(Return(MM_MODEM_GSM_FACILITY_SIM));
+ EXPECT_CALL(*proxy_, GetModemInfo(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetModemInfo));
+ EXPECT_CALL(*gsm_network_proxy_,
+ GetRegistrationInfo(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetRegistrationInfo));
+ EXPECT_CALL(*gsm_network_proxy_, GetSignalQuality(nullptr, _, _))
+ .Times(2)
+ .WillRepeatedly(Invoke(this,
+ &CellularTest::InvokeGetSignalQuality));
+ EXPECT_CALL(*mock_serving_operator_info_, UpdateMCCMNC(_));
+ EXPECT_CALL(*mock_serving_operator_info_, UpdateOperatorName(_));
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ EXPECT_CALL(*modem_info_.mock_manager(), RegisterService(_));
+ AllowCreateGSMCardProxyFromFactory();
+
+ Error error;
+ device_->Start(&error, Bind(&CellularTest::TestCallback, Unretained(this)));
+ EXPECT_TRUE(error.IsSuccess());
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(kIMEI, device_->imei());
+ EXPECT_EQ(kIMSI, device_->imsi());
+ EXPECT_EQ(kTestCarrierSPN, GetCapabilityGSM()->spn_);
+ EXPECT_EQ(kMSISDN, device_->mdn());
+ EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
+ ASSERT_TRUE(device_->service_.get());
+ EXPECT_EQ(kNetworkTechnologyEdge, device_->service_->network_technology());
+ EXPECT_TRUE(GetCapabilityGSM()->sim_lock_status_.enabled);
+ EXPECT_EQ(kStrength, device_->service_->strength());
+ EXPECT_EQ(kRoamingStateRoaming, device_->service_->roaming_state());
+}
+
+TEST_F(CellularTest, StartConnected) {
+ EXPECT_CALL(device_info_, GetFlags(device_->interface_index(), _))
+ .WillOnce(Return(true));
+ SetCellularType(Cellular::kTypeCDMA);
+ device_->set_modem_state(Cellular::kModemStateConnected);
+ device_->set_meid(kMEID);
+ ExpectCdmaStartModem(kNetworkTechnologyEvdo);
+ Error error;
+ device_->Start(&error, Bind(&CellularTest::TestCallback, Unretained(this)));
+ EXPECT_TRUE(error.IsSuccess());
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateConnected, device_->state_);
+}
+
+TEST_F(CellularTest, StartLinked) {
+ EXPECT_CALL(device_info_, GetFlags(device_->interface_index(), _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(IFF_UP), Return(true)));
+ SetCellularType(Cellular::kTypeCDMA);
+ device_->set_modem_state(Cellular::kModemStateConnected);
+ device_->set_meid(kMEID);
+ ExpectCdmaStartModem(kNetworkTechnologyEvdo);
+ EXPECT_CALL(dhcp_provider_, CreateConfig(kTestDeviceName, _, _, _))
+ .WillOnce(Return(dhcp_config_));
+ EXPECT_CALL(*dhcp_config_, RequestIP()).WillOnce(Return(true));
+ EXPECT_CALL(*modem_info_.mock_manager(), UpdateService(_)).Times(3);
+ Error error;
+ device_->Start(&error, Bind(&CellularTest::TestCallback, Unretained(this)));
+ EXPECT_TRUE(error.IsSuccess());
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateLinked, device_->state_);
+ EXPECT_EQ(Service::kStateConfiguring, device_->service_->state());
+ device_->SelectService(nullptr);
+}
+
+TEST_F(CellularTest, FriendlyServiceName) {
+ // Test that the name created for the service is sensible under different
+ // scenarios w.r.t. information about the mobile network operator.
+ SetMockMobileOperatorInfoObjects();
+ CHECK(mock_home_provider_info_);
+ CHECK(mock_serving_operator_info_);
+
+ SetCellularType(Cellular::kTypeCDMA);
+ // We are not testing the behaviour of capabilities here.
+ device_->mobile_operator_info_observer_->set_capability(nullptr);
+
+ // (1) Service created, MNO not known => Default name.
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ device_->CreateService();
+ // Compare substrings explicitly using EXPECT_EQ for better error message.
+ size_t prefix_len = strlen(Cellular::kGenericServiceNamePrefix);
+ EXPECT_EQ(Cellular::kGenericServiceNamePrefix,
+ device_->service_->friendly_name().substr(0, prefix_len));
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (2) Service created, then home provider determined => Name provided by
+ // home provider.
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ device_->CreateService();
+ // Now emulate an event for updated home provider information.
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kHomeProviderName));
+ device_->mobile_operator_info_observer_->OnOperatorChanged();
+ EXPECT_EQ(kHomeProviderName, device_->service_->friendly_name());
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (3) Service created, then serving operator determined => Name provided by
+ // serving operator.
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ device_->CreateService();
+ // Now emulate an event for updated serving operator information.
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kServingOperatorName));
+ device_->mobile_operator_info_observer_->OnOperatorChanged();
+ EXPECT_EQ(kServingOperatorName, device_->service_->friendly_name());
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (4) Service created, then home provider determined, then serving operator
+ // determined => final name is serving operator.
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ device_->CreateService();
+ // Now emulate an event for updated home provider information.
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kHomeProviderName));
+ device_->mobile_operator_info_observer_->OnOperatorChanged();
+ // Now emulate an event for updated serving operator information.
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kServingOperatorName));
+ device_->mobile_operator_info_observer_->OnOperatorChanged();
+ EXPECT_EQ(kServingOperatorName, device_->service_->friendly_name());
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (5) Service created, then serving operator determined, then home provider
+ // determined => final name is serving operator.
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ device_->CreateService();
+ // Now emulate an event for updated serving operator information.
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kServingOperatorName));
+ device_->mobile_operator_info_observer_->OnOperatorChanged();
+ // Now emulate an event for updated home provider information.
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kHomeProviderName));
+ device_->mobile_operator_info_observer_->OnOperatorChanged();
+ EXPECT_EQ(kServingOperatorName, device_->service_->friendly_name());
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (6) Serving operator known, home provider known, and then service created
+ // => Name is serving operator.
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kHomeProviderName));
+ EXPECT_CALL(*mock_serving_operator_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kServingOperatorName));
+ device_->CreateService();
+ EXPECT_EQ(kServingOperatorName, device_->service_->friendly_name());
+}
+
+TEST_F(CellularTest, HomeProviderServingOperator) {
+ // Test that the the home provider information is correctly updated under
+ // different scenarios w.r.t. information about the mobile network operators.
+ SetMockMobileOperatorInfoObjects();
+ CHECK(mock_home_provider_info_);
+ CHECK(mock_serving_operator_info_);
+ Stringmap home_provider;
+ Stringmap serving_operator;
+
+
+ // (1) Neither home provider nor serving operator known.
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+
+ device_->CreateService();
+
+ home_provider = device_->home_provider();
+ VerifyOperatorMap(home_provider, "", "", "");
+ serving_operator = device_->service_->serving_operator();
+ VerifyOperatorMap(serving_operator, "", "", "");
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (2) serving operator known.
+ // When home provider is not known, serving operator proxies in.
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, mccmnc())
+ .WillRepeatedly(ReturnRef(kServingOperatorCode));
+ EXPECT_CALL(*mock_serving_operator_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kServingOperatorName));
+ EXPECT_CALL(*mock_serving_operator_info_, country())
+ .WillRepeatedly(ReturnRef(kServingOperatorCountry));
+
+ device_->CreateService();
+
+ home_provider = device_->home_provider();
+ VerifyOperatorMap(home_provider,
+ kServingOperatorCode,
+ kServingOperatorName,
+ kServingOperatorCountry);
+ serving_operator = device_->service_->serving_operator();
+ VerifyOperatorMap(serving_operator,
+ kServingOperatorCode,
+ kServingOperatorName,
+ kServingOperatorCountry);
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (3) home provider known.
+ // When serving operator is not known, home provider proxies in.
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, mccmnc())
+ .WillRepeatedly(ReturnRef(kHomeProviderCode));
+ EXPECT_CALL(*mock_home_provider_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kHomeProviderName));
+ EXPECT_CALL(*mock_home_provider_info_, country())
+ .WillRepeatedly(ReturnRef(kHomeProviderCountry));
+
+ device_->CreateService();
+
+ home_provider = device_->home_provider();
+ VerifyOperatorMap(home_provider,
+ kHomeProviderCode,
+ kHomeProviderName,
+ kHomeProviderCountry);
+ serving_operator = device_->service_->serving_operator();
+ VerifyOperatorMap(serving_operator,
+ kHomeProviderCode,
+ kHomeProviderName,
+ kHomeProviderCountry);
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (4) Serving operator known, home provider known.
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, mccmnc())
+ .WillRepeatedly(ReturnRef(kHomeProviderCode));
+ EXPECT_CALL(*mock_home_provider_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kHomeProviderName));
+ EXPECT_CALL(*mock_home_provider_info_, country())
+ .WillRepeatedly(ReturnRef(kHomeProviderCountry));
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, mccmnc())
+ .WillRepeatedly(ReturnRef(kServingOperatorCode));
+ EXPECT_CALL(*mock_serving_operator_info_, operator_name())
+ .WillRepeatedly(ReturnRef(kServingOperatorName));
+ EXPECT_CALL(*mock_serving_operator_info_, country())
+ .WillRepeatedly(ReturnRef(kServingOperatorCountry));
+
+ device_->CreateService();
+
+ home_provider = device_->home_provider();
+ VerifyOperatorMap(home_provider,
+ kHomeProviderCode,
+ kHomeProviderName,
+ kHomeProviderCountry);
+ serving_operator = device_->service_->serving_operator();
+ VerifyOperatorMap(serving_operator,
+ kServingOperatorCode,
+ kServingOperatorName,
+ kServingOperatorCountry);
+}
+
+static bool IllegalChar(char a) {
+ return !(isalnum(a) || a == '_');
+}
+
+TEST_F(CellularTest, StorageIdentifier) {
+ // Test that the storage identifier name used by the service is sensible under
+ // different scenarios w.r.t. information about the mobile network operator.
+ SetMockMobileOperatorInfoObjects();
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ CHECK(mock_home_provider_info_);
+ CHECK(mock_serving_operator_info_);
+
+ // See cellular_service.cc
+ string prefix = string(kTypeCellular) + "_" +
+ string(kTestDeviceAddress) + "_";
+ // Service replaces ':' with '_'
+ std::replace_if(prefix.begin(), prefix.end(), &IllegalChar, '_');
+ const string kUuidHomeProvider = "uuidHomeProvider";
+ const string kUuidServingOperator = "uuidServingOperator";
+ const string kSimIdentifier = "12345123451234512345";
+
+ SetCellularType(Cellular::kTypeCDMA);
+ // We are not testing the behaviour of capabilities here.
+ device_->mobile_operator_info_observer_->set_capability(nullptr);
+ ON_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillByDefault(Return(false));
+
+ // (1) Service created, both home provider and serving operator known =>
+ // home provider used.
+ mock_home_provider_info_->SetEmptyDefaultsForProperties();
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_home_provider_info_, uuid())
+ .WillRepeatedly(ReturnRef(kUuidHomeProvider));
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, uuid())
+ .WillRepeatedly(ReturnRef(kUuidServingOperator));
+ device_->CreateService();
+ EXPECT_EQ(prefix + kUuidHomeProvider,
+ device_->service()->GetStorageIdentifier());
+ Mock::VerifyAndClearExpectations(mock_home_provider_info_);
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // Common expectation for following tests:
+ EXPECT_CALL(*mock_home_provider_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+
+ // (2) Service created, no extra information => Default storage_id;
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(false));
+ device_->CreateService();
+ EXPECT_EQ(prefix + device_->service()->friendly_name(),
+ device_->service()->GetStorageIdentifier());
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (3) Service created, serving operator known, uuid known.
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_serving_operator_info_, uuid())
+ .WillRepeatedly(ReturnRef(kUuidServingOperator));
+ device_->CreateService();
+ EXPECT_EQ(prefix + kUuidServingOperator,
+ device_->service()->GetStorageIdentifier());
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+
+ // (4) Service created, serving operator known, uuid not known, iccid known.
+ mock_serving_operator_info_->SetEmptyDefaultsForProperties();
+ EXPECT_CALL(*mock_serving_operator_info_, IsMobileNetworkOperatorKnown())
+ .WillRepeatedly(Return(true));
+ device_->set_sim_identifier(kSimIdentifier);
+ device_->CreateService();
+ EXPECT_EQ(prefix + kSimIdentifier,
+ device_->service()->GetStorageIdentifier());
+ Mock::VerifyAndClearExpectations(mock_serving_operator_info_);
+ device_->DestroyService();
+}
+
+namespace {
+
+MATCHER(ContainsPhoneNumber, "") {
+ return ContainsKey(arg,
+ CellularCapabilityClassic::kConnectPropertyPhoneNumber);
+}
+
+} // namespace
+
+TEST_F(CellularTest, Connect) {
+ Error error;
+ EXPECT_CALL(device_info_, GetFlags(device_->interface_index(), _))
+ .Times(2)
+ .WillRepeatedly(Return(true));
+ device_->state_ = Cellular::kStateConnected;
+ device_->Connect(&error);
+ EXPECT_EQ(Error::kAlreadyConnected, error.type());
+ error.Populate(Error::kSuccess);
+
+ device_->state_ = Cellular::kStateLinked;
+ device_->Connect(&error);
+ EXPECT_EQ(Error::kAlreadyConnected, error.type());
+
+ device_->state_ = Cellular::kStateEnabled;
+ device_->Connect(&error);
+ EXPECT_EQ(Error::kNotRegistered, error.type());
+
+ error.Reset();
+ device_->state_ = Cellular::kStateDisabled;
+ device_->Connect(&error);
+ EXPECT_EQ(Error::kNotRegistered, error.type());
+
+ device_->state_ = Cellular::kStateRegistered;
+ SetService();
+
+ device_->allow_roaming_ = false;
+ device_->service_->roaming_state_ = kRoamingStateRoaming;
+ device_->Connect(&error);
+ EXPECT_EQ(Error::kNotOnHomeNetwork, error.type());
+
+ error.Populate(Error::kSuccess);
+ EXPECT_CALL(*simple_proxy_,
+ Connect(ContainsPhoneNumber(), _, _,
+ CellularCapability::kTimeoutConnect))
+ .Times(2)
+ .WillRepeatedly(Invoke(this, &CellularTest::InvokeConnect));
+ GetCapabilityClassic()->simple_proxy_.reset(simple_proxy_.release());
+ device_->service_->roaming_state_ = kRoamingStateHome;
+ device_->state_ = Cellular::kStateRegistered;
+ device_->Connect(&error);
+ EXPECT_TRUE(error.IsSuccess());
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateConnected, device_->state_);
+
+ device_->allow_roaming_ = true;
+ device_->service_->roaming_state_ = kRoamingStateRoaming;
+ device_->state_ = Cellular::kStateRegistered;
+ device_->Connect(&error);
+ EXPECT_TRUE(error.IsSuccess());
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateConnected, device_->state_);
+}
+
+TEST_F(CellularTest, Disconnect) {
+ Error error;
+ device_->state_ = Cellular::kStateRegistered;
+ device_->Disconnect(&error, "in test");
+ EXPECT_EQ(Error::kNotConnected, error.type());
+ error.Reset();
+
+ device_->state_ = Cellular::kStateConnected;
+ EXPECT_CALL(*proxy_,
+ Disconnect(_, _, CellularCapability::kTimeoutDisconnect))
+ .WillOnce(Invoke(this, &CellularTest::InvokeDisconnect));
+ GetCapabilityClassic()->proxy_.reset(proxy_.release());
+ device_->Disconnect(&error, "in test");
+ EXPECT_TRUE(error.IsSuccess());
+ EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
+}
+
+TEST_F(CellularTest, DisconnectFailure) {
+ // Test the case where the underlying modem state is set
+ // to disconnecting, but shill thinks it's still connected
+ Error error;
+ device_->state_ = Cellular::kStateConnected;
+ EXPECT_CALL(*proxy_,
+ Disconnect(_, _, CellularCapability::kTimeoutDisconnect))
+ .Times(2)
+ .WillRepeatedly(Invoke(this, &CellularTest::InvokeDisconnectFail));
+ GetCapabilityClassic()->proxy_.reset(proxy_.release());
+ device_->modem_state_ = Cellular::kModemStateDisconnecting;
+ device_->Disconnect(&error, "in test");
+ EXPECT_TRUE(error.IsFailure());
+ EXPECT_EQ(Cellular::kStateConnected, device_->state_);
+
+ device_->modem_state_ = Cellular::kModemStateConnected;
+ device_->Disconnect(&error, "in test");
+ EXPECT_TRUE(error.IsFailure());
+ EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
+}
+
+TEST_F(CellularTest, ConnectFailure) {
+ SetCellularType(Cellular::kTypeCDMA);
+ device_->state_ = Cellular::kStateRegistered;
+ SetService();
+ ASSERT_EQ(Service::kStateIdle, device_->service_->state());
+ EXPECT_CALL(*simple_proxy_,
+ Connect(_, _, _, CellularCapability::kTimeoutConnect))
+ .WillOnce(Invoke(this, &CellularTest::InvokeConnectFail));
+ GetCapabilityClassic()->simple_proxy_.reset(simple_proxy_.release());
+ Error error;
+ device_->Connect(&error);
+ EXPECT_EQ(Service::kStateFailure, device_->service_->state());
+}
+
+TEST_F(CellularTest, ConnectFailureNoService) {
+ // Make sure we don't crash if the connect failed and there is no
+ // CellularService object. This can happen if the modem is enabled and
+ // then quick disabled.
+ SetCellularType(Cellular::kTypeCDMA);
+ device_->state_ = Cellular::kStateRegistered;
+ SetService();
+ EXPECT_CALL(
+ *simple_proxy_,
+ Connect(_, _, _, CellularCapability::kTimeoutConnect))
+ .WillOnce(Invoke(this, &CellularTest::InvokeConnectFailNoService));
+ EXPECT_CALL(*modem_info_.mock_manager(), UpdateService(_));
+ GetCapabilityClassic()->simple_proxy_.reset(simple_proxy_.release());
+ Error error;
+ 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.
+ device_->state_ = Cellular::kStateLinked;
+ CellularService *service = SetService();
+ device_->LinkEvent(0, 0); // flags doesn't contain IFF_UP
+ EXPECT_EQ(device_->state_, Cellular::kStateConnected);
+ EXPECT_EQ(device_->service_, service);
+}
+
+TEST_F(CellularTest, UseNoArpGateway) {
+ EXPECT_CALL(dhcp_provider_, CreateConfig(kTestDeviceName, _, _, false))
+ .WillOnce(Return(dhcp_config_));
+ device_->AcquireIPConfig();
+}
+
+TEST_F(CellularTest, ModemStateChangeEnable) {
+ EXPECT_CALL(*simple_proxy_,
+ GetModemStatus(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
+ EXPECT_CALL(*cdma_proxy_, MEID()).WillOnce(Return(kMEID));
+ EXPECT_CALL(*proxy_,
+ GetModemInfo(_, _, CellularCapability::kTimeoutDefault))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetModemInfo));
+ EXPECT_CALL(*cdma_proxy_, GetRegistrationState(nullptr, _, _))
+ .WillOnce(Invoke(this,
+ &CellularTest::InvokeGetRegistrationStateUnregistered));
+ EXPECT_CALL(*cdma_proxy_, GetSignalQuality(nullptr, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeGetSignalQuality));
+ EXPECT_CALL(*modem_info_.mock_manager(), UpdateEnabledTechnologies());
+ device_->state_ = Cellular::kStateDisabled;
+ device_->set_modem_state(Cellular::kModemStateDisabled);
+ SetCellularType(Cellular::kTypeCDMA);
+
+ DBusPropertiesMap props;
+ props[CellularCapabilityClassic::kModemPropertyEnabled].writer().
+ append_bool(true);
+ device_->OnDBusPropertiesChanged(MM_MODEM_INTERFACE, props, vector<string>());
+ dispatcher_.DispatchPendingEvents();
+
+ EXPECT_EQ(Cellular::kModemStateEnabled, device_->modem_state());
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state());
+ EXPECT_TRUE(device_->enabled());
+}
+
+TEST_F(CellularTest, ModemStateChangeDisable) {
+ EXPECT_CALL(*proxy_,
+ Disconnect(_, _, CellularCapability::kTimeoutDisconnect))
+ .WillOnce(Invoke(this, &CellularTest::InvokeDisconnect));
+ EXPECT_CALL(*proxy_,
+ Enable(false, _, _, CellularCapability::kTimeoutEnable))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+ EXPECT_CALL(*modem_info_.mock_manager(), UpdateEnabledTechnologies());
+ device_->enabled_ = true;
+ device_->enabled_pending_ = true;
+ device_->state_ = Cellular::kStateEnabled;
+ device_->set_modem_state(Cellular::kModemStateEnabled);
+ SetCellularType(Cellular::kTypeCDMA);
+ GetCapabilityClassic()->InitProxies();
+
+ GetCapabilityClassic()->OnModemStateChangedSignal(kModemClassicStateEnabled,
+ kModemClassicStateDisabled,
+ 0);
+ dispatcher_.DispatchPendingEvents();
+
+ EXPECT_EQ(Cellular::kModemStateDisabled, device_->modem_state());
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state());
+ EXPECT_FALSE(device_->enabled());
+}
+
+TEST_F(CellularTest, ModemStateChangeStaleConnected) {
+ // Test to make sure that we ignore stale modem Connected state transitions.
+ // When a modem is asked to connect and before the connect completes, the
+ // modem is disabled, it may send a stale Connected state transition after
+ // it has been disabled.
+ AllowCreateGSMCardProxyFromFactory();
+ device_->state_ = Cellular::kStateDisabled;
+ device_->modem_state_ = Cellular::kModemStateEnabling;
+ device_->OnModemStateChanged(Cellular::kModemStateConnected);
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state());
+}
+
+TEST_F(CellularTest, ModemStateChangeValidConnected) {
+ device_->state_ = Cellular::kStateEnabled;
+ device_->modem_state_ = Cellular::kModemStateConnecting;
+ SetService();
+ device_->OnModemStateChanged(Cellular::kModemStateConnected);
+ EXPECT_EQ(Cellular::kStateConnected, device_->state());
+}
+
+TEST_F(CellularTest, ModemStateChangeLostRegistration) {
+ SetCellularType(Cellular::kTypeUniversal);
+ CellularCapabilityUniversal *capability = GetCapabilityUniversal();
+ capability->registration_state_ = MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
+ EXPECT_TRUE(capability->IsRegistered());
+ device_->set_modem_state(Cellular::kModemStateRegistered);
+ device_->OnModemStateChanged(Cellular::kModemStateEnabled);
+ EXPECT_FALSE(capability->IsRegistered());
+}
+
+TEST_F(CellularTest, StartModemCallback) {
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ EXPECT_EQ(device_->state_, Cellular::kStateDisabled);
+ device_->StartModemCallback(Bind(&CellularTest::TestCallback,
+ Unretained(this)),
+ Error(Error::kSuccess));
+ EXPECT_EQ(device_->state_, Cellular::kStateEnabled);
+}
+
+TEST_F(CellularTest, StartModemCallbackFail) {
+ EXPECT_CALL(*this, TestCallback(IsFailure()));
+ EXPECT_EQ(device_->state_, Cellular::kStateDisabled);
+ device_->StartModemCallback(Bind(&CellularTest::TestCallback,
+ Unretained(this)),
+ Error(Error::kOperationFailed));
+ EXPECT_EQ(device_->state_, Cellular::kStateDisabled);
+}
+
+TEST_F(CellularTest, StopModemCallback) {
+ EXPECT_CALL(*this, TestCallback(IsSuccess()));
+ SetMockService();
+ device_->StopModemCallback(Bind(&CellularTest::TestCallback,
+ Unretained(this)),
+ Error(Error::kSuccess));
+ EXPECT_EQ(device_->state_, Cellular::kStateDisabled);
+ EXPECT_FALSE(device_->service_.get());
+}
+
+TEST_F(CellularTest, StopModemCallbackFail) {
+ EXPECT_CALL(*this, TestCallback(IsFailure()));
+ SetMockService();
+ device_->StopModemCallback(Bind(&CellularTest::TestCallback,
+ Unretained(this)),
+ Error(Error::kOperationFailed));
+ EXPECT_EQ(device_->state_, Cellular::kStateDisabled);
+ EXPECT_FALSE(device_->service_.get());
+}
+
+TEST_F(CellularTest, SetAllowRoaming) {
+ EXPECT_FALSE(device_->allow_roaming_);
+ EXPECT_CALL(*modem_info_.mock_manager(), UpdateDevice(_));
+ Error error;
+ device_->SetAllowRoaming(true, &error);
+ EXPECT_TRUE(error.IsSuccess());
+ EXPECT_TRUE(device_->allow_roaming_);
+}
+
+class TestRPCTaskDelegate :
+ public RPCTaskDelegate,
+ public base::SupportsWeakPtr<TestRPCTaskDelegate> {
+ public:
+ virtual void GetLogin(std::string *user, std::string *password) {}
+ virtual void Notify(const std::string &reason,
+ const std::map<std::string, std::string> &dict) {}
+};
+
+TEST_F(CellularTest, LinkEventUpWithPPP) {
+ // If PPP is running, don't run DHCP as well.
+ TestRPCTaskDelegate task_delegate;
+ base::Callback<void(pid_t, int)> death_callback;
+ unique_ptr<NiceMock<MockExternalTask>> mock_task(
+ new NiceMock<MockExternalTask>(modem_info_.control_interface(),
+ modem_info_.glib(),
+ task_delegate.AsWeakPtr(),
+ death_callback));
+ EXPECT_CALL(*mock_task, OnDelete()).Times(AnyNumber());
+ device_->ppp_task_ = std::move(mock_task);
+ device_->state_ = Cellular::kStateConnected;
+ EXPECT_CALL(dhcp_provider_, CreateConfig(kTestDeviceName, _, _, _))
+ .Times(0);
+ EXPECT_CALL(*dhcp_config_, RequestIP()).Times(0);
+ device_->LinkEvent(IFF_UP, 0);
+}
+
+TEST_F(CellularTest, LinkEventUpWithoutPPP) {
+ // If PPP is not running, fire up DHCP.
+ device_->state_ = Cellular::kStateConnected;
+ EXPECT_CALL(dhcp_provider_, CreateConfig(kTestDeviceName, _, _, _))
+ .WillOnce(Return(dhcp_config_));
+ EXPECT_CALL(*dhcp_config_, RequestIP());
+ EXPECT_CALL(*dhcp_config_, ReleaseIP(_)).Times(AnyNumber());
+ device_->LinkEvent(IFF_UP, 0);
+}
+
+TEST_F(CellularTest, StartPPP) {
+ const int kPID = 234;
+ EXPECT_EQ(nullptr, device_->ppp_task_);
+ StartPPP(kPID);
+}
+
+TEST_F(CellularTest, StartPPPAlreadyStarted) {
+ const int kPID = 234;
+ StartPPP(kPID);
+
+ const int kPID2 = 235;
+ StartPPP(kPID2);
+}
+
+TEST_F(CellularTest, StartPPPAfterEthernetUp) {
+ CellularService *service(SetService());
+ device_->state_ = Cellular::kStateLinked;
+ device_->set_ipconfig(dhcp_config_);
+ device_->SelectService(service);
+ EXPECT_CALL(*dhcp_config_, ReleaseIP(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(true));
+ const int kPID = 234;
+ EXPECT_EQ(nullptr, device_->ppp_task_);
+ StartPPP(kPID);
+ EXPECT_EQ(Cellular::kStateLinked, device_->state());
+}
+
+TEST_F(CellularTest, GetLogin) {
+ // Doesn't crash when there is no service.
+ string username_to_pppd;
+ string password_to_pppd;
+ EXPECT_FALSE(device_->service());
+ device_->GetLogin(&username_to_pppd, &password_to_pppd);
+
+ // Provides expected username and password in normal case.
+ const char kFakeUsername[] = "fake-user";
+ const char kFakePassword[] = "fake-password";
+ CellularService &service(*SetService());
+ service.ppp_username_ = kFakeUsername;
+ service.ppp_password_ = kFakePassword;
+ device_->GetLogin(&username_to_pppd, &password_to_pppd);
+}
+
+TEST_F(CellularTest, Notify) {
+ // Common setup.
+ MockPPPDeviceFactory *ppp_device_factory =
+ MockPPPDeviceFactory::GetInstance();
+ const int kPID = 91;
+ device_->ppp_device_factory_ = ppp_device_factory;
+ SetMockService();
+ StartPPP(kPID);
+
+ const map<string, string> kEmptyArgs;
+ device_->Notify(kPPPReasonAuthenticating, kEmptyArgs);
+ EXPECT_TRUE(device_->is_ppp_authenticating_);
+ device_->Notify(kPPPReasonAuthenticated, kEmptyArgs);
+ EXPECT_FALSE(device_->is_ppp_authenticating_);
+
+ // Normal connect.
+ const string kInterfaceName("fake-device");
+ const int kInterfaceIndex = 1;
+ scoped_refptr<MockPPPDevice> ppp_device;
+ map<string, string> ppp_config;
+ ppp_device =
+ new MockPPPDevice(modem_info_.control_interface(), nullptr, nullptr,
+ nullptr, kInterfaceName, kInterfaceIndex);
+ ppp_config[kPPPInterfaceName] = kInterfaceName;
+ EXPECT_CALL(device_info_, GetIndex(kInterfaceName))
+ .WillOnce(Return(kInterfaceIndex));
+ EXPECT_CALL(device_info_, RegisterDevice(_));
+ EXPECT_CALL(*ppp_device_factory,
+ CreatePPPDevice(_, _, _, _, kInterfaceName, kInterfaceIndex))
+ .WillOnce(Return(ppp_device));
+ EXPECT_CALL(*ppp_device, SetEnabled(true));
+ EXPECT_CALL(*ppp_device, SelectService(_));
+ EXPECT_CALL(*ppp_device, UpdateIPConfigFromPPP(ppp_config, false));
+ device_->Notify(kPPPReasonConnect, ppp_config);
+ Mock::VerifyAndClearExpectations(&device_info_);
+ Mock::VerifyAndClearExpectations(ppp_device);
+
+ // Re-connect on same network device: if pppd sends us multiple connect
+ // events, we behave sanely.
+ EXPECT_CALL(device_info_, GetIndex(kInterfaceName))
+ .WillOnce(Return(kInterfaceIndex));
+ EXPECT_CALL(*ppp_device, SetEnabled(true));
+ EXPECT_CALL(*ppp_device, SelectService(_));
+ EXPECT_CALL(*ppp_device, UpdateIPConfigFromPPP(ppp_config, false));
+ device_->Notify(kPPPReasonConnect, ppp_config);
+ Mock::VerifyAndClearExpectations(&device_info_);
+ Mock::VerifyAndClearExpectations(ppp_device);
+
+ // Re-connect on new network device: if we still have the PPPDevice
+ // from a prior connect, this new connect should DTRT. This is
+ // probably an unlikely case.
+ const string kInterfaceName2("fake-device2");
+ const int kInterfaceIndex2 = 2;
+ scoped_refptr<MockPPPDevice> ppp_device2;
+ map<string, string> ppp_config2;
+ ppp_device2 =
+ new MockPPPDevice(modem_info_.control_interface(), nullptr, nullptr,
+ nullptr, kInterfaceName2, kInterfaceIndex2);
+ ppp_config2[kPPPInterfaceName] = kInterfaceName2;
+ EXPECT_CALL(device_info_, GetIndex(kInterfaceName2))
+ .WillOnce(Return(kInterfaceIndex2));
+ EXPECT_CALL(device_info_,
+ RegisterDevice(static_cast<DeviceRefPtr>(ppp_device2)));
+ EXPECT_CALL(*ppp_device_factory,
+ CreatePPPDevice(_, _, _, _, kInterfaceName2, kInterfaceIndex2))
+ .WillOnce(Return(ppp_device2));
+ EXPECT_CALL(*ppp_device, SelectService(ServiceRefPtr(nullptr)));
+ EXPECT_CALL(*ppp_device2, SetEnabled(true));
+ EXPECT_CALL(*ppp_device2, SelectService(_));
+ EXPECT_CALL(*ppp_device2, UpdateIPConfigFromPPP(ppp_config2, false));
+ device_->Notify(kPPPReasonConnect, ppp_config2);
+ Mock::VerifyAndClearExpectations(&device_info_);
+ Mock::VerifyAndClearExpectations(ppp_device);
+ Mock::VerifyAndClearExpectations(ppp_device2);
+
+ // Disconnect should report unknown failure, since we had a
+ // Notify(kPPPReasonAuthenticated, ...).
+ EXPECT_CALL(*ppp_device2, SetServiceFailure(Service::kFailureUnknown));
+ device_->Notify(kPPPReasonDisconnect, kEmptyArgs);
+ EXPECT_EQ(nullptr, device_->ppp_task_);
+
+ // |Cellular::ppp_task_| is destroyed on the task loop. Must dispatch once to
+ // cleanup.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularTest, PPPConnectionFailedBeforeAuth) {
+ // Test that we properly set Service state in the case where pppd
+ // disconnects before authenticating (as opposed to the Notify test,
+ // where pppd disconnects after connecting).
+ const int kPID = 52;
+ const map<string, string> kEmptyArgs;
+ MockCellularService *service = SetMockService();
+ StartPPP(kPID);
+
+ ExpectDisconnectCapabilityUniversal();
+ EXPECT_CALL(*service, SetFailure(Service::kFailureUnknown));
+ device_->Notify(kPPPReasonDisconnect, kEmptyArgs);
+ EXPECT_EQ(nullptr, device_->ppp_task_);
+ VerifyDisconnect();
+
+ // |Cellular::ppp_task_| is destroyed on the task loop. Must dispatch once to
+ // cleanup.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularTest, PPPConnectionFailedDuringAuth) {
+ // Test that we properly set Service state in the case where pppd
+ // disconnects during authentication (as opposed to the Notify test,
+ // where pppd disconnects after connecting).
+ const int kPID = 52;
+ const map<string, string> kEmptyArgs;
+ MockCellularService *service = SetMockService();
+ StartPPP(kPID);
+
+ ExpectDisconnectCapabilityUniversal();
+ EXPECT_CALL(*service, SetFailure(Service::kFailurePPPAuth));
+ device_->Notify(kPPPReasonAuthenticating, kEmptyArgs);
+ device_->Notify(kPPPReasonDisconnect, kEmptyArgs);
+ EXPECT_EQ(nullptr, device_->ppp_task_);
+ VerifyDisconnect();
+
+ // |Cellular::ppp_task_| is destroyed on the task loop. Must dispatch once to
+ // cleanup.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularTest, PPPConnectionFailedAfterAuth) {
+ // Test that we properly set Service state in the case where pppd
+ // disconnects after authenticating, but before connecting (as
+ // opposed to the Notify test, where pppd disconnects after
+ // connecting).
+ const int kPID = 52;
+ const map<string, string> kEmptyArgs;
+ MockCellularService *service = SetMockService();
+ StartPPP(kPID);
+
+ EXPECT_CALL(*service, SetFailure(Service::kFailureUnknown));
+ ExpectDisconnectCapabilityUniversal();
+ device_->Notify(kPPPReasonAuthenticating, kEmptyArgs);
+ device_->Notify(kPPPReasonAuthenticated, kEmptyArgs);
+ device_->Notify(kPPPReasonDisconnect, kEmptyArgs);
+ EXPECT_EQ(nullptr, device_->ppp_task_);
+ VerifyDisconnect();
+
+ // |Cellular::ppp_task_| is destroyed on the task loop. Must dispatch once to
+ // cleanup.
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(CellularTest, OnPPPDied) {
+ const int kPID = 1234;
+ const int kExitStatus = 5;
+ ExpectDisconnectCapabilityUniversal();
+ device_->OnPPPDied(kPID, kExitStatus);
+ VerifyDisconnect();
+}
+
+TEST_F(CellularTest, DropConnection) {
+ device_->set_ipconfig(dhcp_config_);
+ EXPECT_CALL(*dhcp_config_, ReleaseIP(_));
+ device_->DropConnection();
+ Mock::VerifyAndClearExpectations(dhcp_config_); // verify before dtor
+ EXPECT_FALSE(device_->ipconfig());
+}
+
+TEST_F(CellularTest, DropConnectionPPP) {
+ scoped_refptr<MockPPPDevice> ppp_device(
+ new MockPPPDevice(modem_info_.control_interface(),
+ nullptr, nullptr, nullptr, "fake_ppp0", -1));
+ EXPECT_CALL(*ppp_device, DropConnection());
+ device_->ppp_device_ = ppp_device;
+ device_->DropConnection();
+}
+
+TEST_F(CellularTest, ChangeServiceState) {
+ MockCellularService *service(SetMockService());
+ EXPECT_CALL(*service, SetState(_));
+ EXPECT_CALL(*service, SetFailure(_));
+ EXPECT_CALL(*service, SetFailureSilent(_));
+ ON_CALL(*service, state()).WillByDefault(Return(Service::kStateUnknown));
+
+ // Without PPP, these should be handled by our selected_service().
+ device_->SelectService(service);
+ device_->SetServiceState(Service::kStateConfiguring);
+ device_->SetServiceFailure(Service::kFailurePPPAuth);
+ device_->SetServiceFailureSilent(Service::kFailureUnknown);
+ Mock::VerifyAndClearExpectations(service); // before Cellular dtor
+}
+
+TEST_F(CellularTest, ChangeServiceStatePPP) {
+ MockCellularService *service(SetMockService());
+ scoped_refptr<MockPPPDevice> ppp_device(
+ new MockPPPDevice(modem_info_.control_interface(),
+ nullptr, nullptr, nullptr, "fake_ppp0", -1));
+ EXPECT_CALL(*ppp_device, SetServiceState(_));
+ EXPECT_CALL(*ppp_device, SetServiceFailure(_));
+ EXPECT_CALL(*ppp_device, SetServiceFailureSilent(_));
+ EXPECT_CALL(*service, SetState(_)).Times(0);
+ EXPECT_CALL(*service, SetFailure(_)).Times(0);
+ EXPECT_CALL(*service, SetFailureSilent(_)).Times(0);
+ device_->ppp_device_ = ppp_device;
+
+ // With PPP, these should all be punted over to the |ppp_device|.
+ // Note in particular that Cellular does not manipulate |service| in
+ // this case.
+ device_->SetServiceState(Service::kStateConfiguring);
+ device_->SetServiceFailure(Service::kFailurePPPAuth);
+ device_->SetServiceFailureSilent(Service::kFailureUnknown);
+}
+
+TEST_F(CellularTest, StopPPPOnDisconnect) {
+ const int kPID = 123;
+ Error error;
+ StartPPP(kPID);
+ FakeUpConnectedPPP();
+ ExpectPPPStopped();
+ device_->Disconnect(&error, "in test");
+ VerifyPPPStopped();
+}
+
+TEST_F(CellularTest, StopPPPOnSuspend) {
+ const int kPID = 123;
+ StartPPP(kPID);
+ FakeUpConnectedPPP();
+ ExpectPPPStopped();
+ device_->OnBeforeSuspend(ResultCallback());
+ VerifyPPPStopped();
+}
+
+TEST_F(CellularTest, OnAfterResumeDisabledWantDisabled) {
+ // The Device was disabled prior to resume, and the profile settings
+ // indicate that the device should be disabled. We should leave
+ // things alone.
+
+ // Initial state.
+ mm1::MockModemProxy *mm1_proxy = SetupOnAfterResume();
+ set_enabled_persistent(false);
+ EXPECT_FALSE(device_->running());
+ EXPECT_FALSE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state_);
+
+ // Resume, while device is disabled.
+ EXPECT_CALL(*mm1_proxy, Enable(_, _, _, _)).Times(0);
+ device_->OnAfterResume();
+ EXPECT_FALSE(device_->running());
+ EXPECT_FALSE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state_);
+}
+
+TEST_F(CellularTest, OnAfterResumeDisableInProgressWantDisabled) {
+ // The Device was not disabled prior to resume, but the profile
+ // settings indicate that the device _should be_ disabled. Most
+ // likely, we started disabling the device, but that did not
+ // complete before we suspended. We should leave things alone.
+
+ // Initial state.
+ mm1::MockModemProxy *mm1_proxy = SetupOnAfterResume();
+ Error error;
+ EXPECT_CALL(*mm1_proxy, Enable(true, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+ device_->SetEnabled(true);
+ EXPECT_TRUE(device_->running());
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
+
+ // Start disable.
+ EXPECT_CALL(*modem_info_.mock_manager(), UpdateDevice(_));
+ device_->SetEnabledPersistent(false, &error, ResultCallback());
+ EXPECT_FALSE(device_->running()); // changes immediately
+ EXPECT_FALSE(device_->enabled_persistent()); // changes immediately
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_); // changes on completion
+
+ // Resume, with disable still in progress.
+ device_->OnAfterResume();
+ EXPECT_FALSE(device_->running());
+ EXPECT_FALSE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
+
+ // Finish the disable operation.
+ EXPECT_CALL(*mm1_proxy, Enable(false, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+ EXPECT_CALL(*mm1_proxy, SetPowerState(_, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeSetPowerState));
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_FALSE(device_->running());
+ EXPECT_FALSE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state_);
+}
+
+TEST_F(CellularTest, OnAfterResumeDisableQueuedWantEnabled) {
+ // The Device was not disabled prior to resume, and the profile
+ // settings indicate that the device should be enabled. In
+ // particular, we went into suspend before we actually processed the
+ // task queued by CellularCapabilityUniversal::StopModem.
+ //
+ // This is unlikely, and a case where we fail to do the right thing.
+ // The tests exists to document this corner case, which we get wrong.
+
+ // Initial state.
+ mm1::MockModemProxy *mm1_proxy = SetupOnAfterResume();
+ EXPECT_CALL(*mm1_proxy, Enable(true, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+ device_->SetEnabled(true);
+ EXPECT_TRUE(device_->running());
+ EXPECT_TRUE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
+
+ // Start disable.
+ device_->SetEnabled(false);
+ EXPECT_FALSE(device_->running()); // changes immediately
+ EXPECT_TRUE(device_->enabled_persistent()); // no change
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_); // changes on completion
+
+ // Refresh proxies, since CellularCapabilityUniversal::StartModem wants
+ // new proxies. Also, stash away references for later.
+ PopulateProxies();
+ SetCommonOnAfterResumeExpectations();
+ mm1_proxy = mm1_proxy_.get();
+ auto dbus_properties_proxy = dbus_properties_proxy_.get();
+
+ // Resume, with disable still in progress.
+ EXPECT_CALL(*mm1_proxy, Enable(true, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnableReturningWrongState));
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_); // disable still pending
+ device_->OnAfterResume();
+ EXPECT_TRUE(device_->running()); // changes immediately
+ EXPECT_TRUE(device_->enabled_persistent()); // no change
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state_); // by OnAfterResume
+
+ // Set up state that we need.
+ DBusPropertiesMap modem_properties;
+ DBus::Variant modem_state;
+ modem_state.writer().append_int32(Cellular::kModemStateDisabled);
+ modem_properties = DBusPropertiesMap{{MM_MODEM_PROPERTY_STATE, modem_state}};
+
+ // Let the disable complete.
+ EXPECT_CALL(*mm1_proxy, Enable(false, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+ EXPECT_CALL(*mm1_proxy, SetPowerState(_, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeSetPowerState));
+ EXPECT_CALL(*dbus_properties_proxy, GetAll(_))
+ .WillRepeatedly(Return(modem_properties));
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_TRUE(device_->running()); // last changed by OnAfterResume
+ EXPECT_TRUE(device_->enabled_persistent()); // last changed by OnAfterResume
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state_);
+
+ // There's nothing queued up to restart the modem. Even though we
+ // want to be running, we're stuck in the disabled state.
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_TRUE(device_->running());
+ EXPECT_TRUE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state_);
+}
+
+TEST_F(CellularTest, OnAfterResumePowerDownInProgressWantEnabled) {
+ // The Device was not fully disabled prior to resume, and the
+ // profile settings indicate that the device should be enabled. In
+ // this case, we have disabled the device, but are waiting for the
+ // power-down (switch to low power) to complete.
+ //
+ // This test emulates the behavior of the Huawei E303 dongle, when
+ // Manager::kTerminationActionsTimeoutMilliseconds is 9500
+ // msec. (The dongle takes 10-11 seconds to go through the whole
+ // disable, power-down sequence).
+ //
+ // Eventually, the power-down would complete, and the device would
+ // be stuck in the disabled state. To counter-act that,
+ // OnAfterResume tries to enable the device now, even though the
+ // device is currently enabled.
+
+ // Initial state.
+ mm1::MockModemProxy *mm1_proxy = SetupOnAfterResume();
+ EXPECT_CALL(*mm1_proxy, Enable(true, _, _, _))
+ .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+ device_->SetEnabled(true);
+ EXPECT_TRUE(device_->running());
+ EXPECT_TRUE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
+
+ // Start disable.
+ ResultCallback modem_proxy_enable_callback;
+ EXPECT_CALL(*mm1_proxy, Enable(false, _, _, _))
+ .WillOnce(SaveArg<2>(&modem_proxy_enable_callback));
+ device_->SetEnabled(false);
+ dispatcher_.DispatchPendingEvents(); // SetEnabled yields a deferred task
+ EXPECT_FALSE(device_->running()); // changes immediately
+ EXPECT_TRUE(device_->enabled_persistent()); // no change
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_); // changes on completion
+
+ // Let the disable complete. That will trigger power-down.
+ //
+ // Note that, unlike for mm1_proxy->Enable, we don't save the
+ // callback for mm1_proxy->SetPowerState. We expect the callback not
+ // to be executed, as explained in the comment about having a fresh
+ // proxy OnAfterResume, below.
+ Error error;
+ ASSERT_TRUE(error.IsSuccess());
+ EXPECT_CALL(*mm1_proxy, SetPowerState(MM_MODEM_POWER_STATE_LOW, _, _, _))
+ .WillOnce(SetErrorTypeInArgument<1>(Error::kOperationInitiated));
+ modem_proxy_enable_callback.Run(error);
+
+ // No response to power-down yet. It probably completed while the host
+ // was asleep, and so the reply from the modem was lost.
+
+ // Refresh proxies, since CellularCapabilityUniversal::StartModem wants
+ // new proxies. Also, stash away references for later.
+ PopulateProxies();
+ SetCommonOnAfterResumeExpectations();
+ auto new_mm1_proxy = mm1_proxy_.get();
+ auto dbus_properties_proxy = dbus_properties_proxy_.get();
+
+ // Resume.
+ ResultCallback new_callback;
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_); // disable still pending
+ EXPECT_CALL(*new_mm1_proxy, Enable(true, _, _, _))
+ .WillOnce(SaveArg<2>(&modem_proxy_enable_callback));
+ device_->OnAfterResume();
+ EXPECT_TRUE(device_->running()); // changes immediately
+ EXPECT_TRUE(device_->enabled_persistent()); // no change
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state_); // by OnAfterResume
+
+ // We should have a fresh proxy OnAfterResume. Otherwise, we may get
+ // confused when the SetPowerState call completes (either naturally,
+ // or via a time-out from dbus-c++).
+ //
+ // The pointers must differ, because the new proxy is constructed
+ // before the old one is destructed.
+ EXPECT_FALSE(new_mm1_proxy == mm1_proxy);
+
+ // Set up state that we need.
+ DBusPropertiesMap modem_properties;
+ DBus::Variant modem_state;
+ modem_state.writer().append_int32(Cellular::kModemStateEnabled);
+ modem_properties = DBusPropertiesMap{{MM_MODEM_PROPERTY_STATE, modem_state}};
+
+ // Let the enable complete.
+ ASSERT_TRUE(error.IsSuccess());
+ EXPECT_CALL(*dbus_properties_proxy, GetAll(_))
+ .WillRepeatedly(Return(modem_properties));
+ ASSERT_TRUE(!modem_proxy_enable_callback.is_null());
+ modem_proxy_enable_callback.Run(error);
+ EXPECT_TRUE(device_->running());
+ EXPECT_TRUE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
+}
+
+TEST_F(CellularTest, OnAfterResumeDisabledWantEnabled) {
+ // This is the ideal case. The disable process completed before
+ // going into suspend.
+ mm1::MockModemProxy *mm1_proxy = SetupOnAfterResume();
+ EXPECT_FALSE(device_->running());
+ EXPECT_TRUE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateDisabled, device_->state_);
+
+ // Resume.
+ ResultCallback modem_proxy_enable_callback;
+ EXPECT_CALL(*mm1_proxy, Enable(true, _, _, _))
+ .WillOnce(SaveArg<2>(&modem_proxy_enable_callback));
+ device_->OnAfterResume();
+
+ // Complete enable.
+ Error error;
+ ASSERT_TRUE(error.IsSuccess());
+ modem_proxy_enable_callback.Run(error);
+ EXPECT_TRUE(device_->running());
+ EXPECT_TRUE(device_->enabled_persistent());
+ EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
+}
+
+// Custom property setters should return false, and make no changes, if
+// the new value is the same as the old value.
+TEST_F(CellularTest, CustomSetterNoopChange) {
+ Error error;
+ EXPECT_FALSE(device_->allow_roaming_);
+ EXPECT_FALSE(device_->SetAllowRoaming(false, &error));
+ EXPECT_TRUE(error.IsSuccess());
+}
+
+TEST_F(CellularTest, ScanImmediateFailure) {
+ Error error;
+
+ device_->set_found_networks(kTestNetworksCellular);
+ EXPECT_FALSE(device_->scanning_);
+ // |InitProxies| must be called before calling any functions on the
+ // Capability*, to set up the modem proxies.
+ // Warning: The test loses all references to the proxies when |InitProxies| is
+ // called.
+ GetCapabilityGSM()->InitProxies();
+ device_->Scan(Device::kFullScan, &error, "");
+ EXPECT_TRUE(error.IsFailure());
+ EXPECT_FALSE(device_->scanning_);
+ EXPECT_EQ(kTestNetworksCellular, device_->found_networks());
+}
+
+TEST_F(CellularTest, ScanAsynchronousFailure) {
+ Error error;
+ ScanResultsCallback results_callback;
+
+ device_->set_found_networks(kTestNetworksCellular);
+ EXPECT_CALL(*gsm_network_proxy_, Scan(&error, _, _))
+ .WillOnce(DoAll(SetErrorTypeInArgument<0>(Error::kOperationInitiated),
+ SaveArg<1>(&results_callback)));
+ EXPECT_FALSE(device_->scanning_);
+ // |InitProxies| must be called before calling any functions on the
+ // Capability*, to set up the modem proxies.
+ // Warning: The test loses all references to the proxies when |InitProxies| is
+ // called.
+ GetCapabilityGSM()->InitProxies();
+ device_->Scan(Device::kFullScan, &error, "");
+ EXPECT_TRUE(error.IsOngoing());
+ EXPECT_TRUE(device_->scanning_);
+
+ // Asynchronously fail the scan.
+ error.Populate(Error::kOperationFailed);
+ results_callback.Run(kTestNetworksGSM, error);
+ EXPECT_FALSE(device_->scanning_);
+ EXPECT_TRUE(device_->found_networks().empty());
+}
+
+TEST_F(CellularTest, ScanSuccess) {
+ Error error;
+ ScanResultsCallback results_callback;
+
+ device_->clear_found_networks();
+ EXPECT_CALL(*gsm_network_proxy_, Scan(&error, _, _))
+ .WillOnce(DoAll(SetErrorTypeInArgument<0>(Error::kOperationInitiated),
+ SaveArg<1>(&results_callback)));
+ EXPECT_FALSE(device_->scanning_);
+ // |InitProxies| must be called before calling any functions on the
+ // Capability*, to set up the modem proxies.
+ // Warning: The test loses all references to the proxies when |InitProxies| is
+ // called.
+ GetCapabilityGSM()->InitProxies();
+ device_->Scan(Device::kFullScan, &error, "");
+ EXPECT_TRUE(error.IsOngoing());
+ EXPECT_TRUE(device_->scanning_);
+
+ // Successfully complete the scan.
+ const GSMScanResults gsm_results{};
+ error.Populate(Error::kSuccess);
+ results_callback.Run(kTestNetworksGSM, error);
+ EXPECT_FALSE(device_->scanning_);
+ EXPECT_EQ(kTestNetworksCellular, device_->found_networks());
+}
+
+TEST_F(CellularTest, EstablishLinkDHCP) {
+ unique_ptr<CellularBearer> bearer(
+ new CellularBearer(&proxy_factory_, "", ""));
+ bearer->set_ipv4_config_method(IPConfig::kMethodDHCP);
+ SetCapabilityUniversalActiveBearer(std::move(bearer));
+ device_->state_ = Cellular::kStateConnected;
+
+ MockCellularService *service = SetMockService();
+ ON_CALL(*service, state()).WillByDefault(Return(Service::kStateUnknown));
+
+ EXPECT_CALL(device_info_, GetFlags(device_->interface_index(), _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(IFF_UP), Return(true)));
+ EXPECT_CALL(dhcp_provider_, CreateConfig(kTestDeviceName, _, _, _))
+ .WillOnce(Return(dhcp_config_));
+ EXPECT_CALL(*dhcp_config_, RequestIP()).WillOnce(Return(true));
+ EXPECT_CALL(*service, SetState(Service::kStateConfiguring));
+ device_->EstablishLink();
+ EXPECT_EQ(service, device_->selected_service());
+ Mock::VerifyAndClearExpectations(service); // before Cellular dtor
+}
+
+TEST_F(CellularTest, EstablishLinkPPP) {
+ unique_ptr<CellularBearer> bearer(
+ new CellularBearer(&proxy_factory_, "", ""));
+ bearer->set_ipv4_config_method(IPConfig::kMethodPPP);
+ SetCapabilityUniversalActiveBearer(std::move(bearer));
+ device_->state_ = Cellular::kStateConnected;
+
+ const int kPID = 123;
+ MockGLib &mock_glib(*dynamic_cast<MockGLib *>(modem_info_.glib()));
+ EXPECT_CALL(mock_glib, ChildWatchAdd(kPID, _, _));
+ EXPECT_CALL(mock_glib, SpawnAsync(_, _, _, _, _, _, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<6>(kPID), Return(true)));
+ device_->EstablishLink();
+ EXPECT_FALSE(device_->ipconfig()); // No DHCP client.
+ EXPECT_FALSE(device_->selected_service());
+ EXPECT_FALSE(device_->is_ppp_authenticating_);
+ EXPECT_NE(nullptr, device_->ppp_task_);
+}
+
+TEST_F(CellularTest, EstablishLinkStatic) {
+ IPAddress::Family kAddressFamily = IPAddress::kFamilyIPv4;
+ const char kAddress[] = "10.0.0.1";
+ const char kGateway[] = "10.0.0.254";
+ const int32_t kSubnetPrefix = 16;
+ const char *const kDNS[] = {"10.0.0.2", "8.8.4.4", "8.8.8.8"};
+
+ unique_ptr<IPConfig::Properties> ipconfig_properties(
+ new IPConfig::Properties);
+ ipconfig_properties->address_family = kAddressFamily;
+ ipconfig_properties->address = kAddress;
+ ipconfig_properties->gateway = kGateway;
+ ipconfig_properties->subnet_prefix = kSubnetPrefix;
+ ipconfig_properties->dns_servers = vector<string>{kDNS[0], kDNS[1], kDNS[2]};
+
+ unique_ptr<CellularBearer> bearer(
+ new CellularBearer(&proxy_factory_, "", ""));
+ bearer->set_ipv4_config_method(IPConfig::kMethodStatic);
+ bearer->set_ipv4_config_properties(std::move(ipconfig_properties));
+ SetCapabilityUniversalActiveBearer(std::move(bearer));
+ device_->state_ = Cellular::kStateConnected;
+
+ MockCellularService *service = SetMockService();
+ ON_CALL(*service, state()).WillByDefault(Return(Service::kStateUnknown));
+
+ EXPECT_CALL(device_info_, GetFlags(device_->interface_index(), _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(IFF_UP), Return(true)));
+ EXPECT_CALL(*service, SetState(Service::kStateConfiguring));
+ device_->EstablishLink();
+ EXPECT_EQ(service, device_->selected_service());
+ ASSERT_TRUE(device_->ipconfig());
+ EXPECT_EQ(kAddressFamily, device_->ipconfig()->properties().address_family);
+ EXPECT_EQ(kAddress, device_->ipconfig()->properties().address);
+ EXPECT_EQ(kGateway, device_->ipconfig()->properties().gateway);
+ EXPECT_EQ(kSubnetPrefix, device_->ipconfig()->properties().subnet_prefix);
+ ASSERT_EQ(3, device_->ipconfig()->properties().dns_servers.size());
+ EXPECT_EQ(kDNS[0], device_->ipconfig()->properties().dns_servers[0]);
+ EXPECT_EQ(kDNS[1], device_->ipconfig()->properties().dns_servers[1]);
+ EXPECT_EQ(kDNS[2], device_->ipconfig()->properties().dns_servers[2]);
+ Mock::VerifyAndClearExpectations(service); // before Cellular dtor
+}
+
+} // namespace shill
diff --git a/cellular/dbus_objectmanager_proxy.cc b/cellular/dbus_objectmanager_proxy.cc
new file mode 100644
index 0000000..7479cfd
--- /dev/null
+++ b/cellular/dbus_objectmanager_proxy.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 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/dbus_objectmanager_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+
+namespace shill {
+
+DBusObjectManagerProxy::DBusObjectManagerProxy(
+ DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+DBusObjectManagerProxy::~DBusObjectManagerProxy() {}
+
+void DBusObjectManagerProxy::GetManagedObjects(
+ Error *error,
+ const ManagedObjectsCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetManagedObjectsAsync, callback,
+ error, &CellularError::FromDBusError, timeout);
+}
+
+void DBusObjectManagerProxy::set_interfaces_added_callback(
+ const InterfacesAddedSignalCallback &callback) {
+ proxy_.set_interfaces_added_callback(callback);
+}
+
+void DBusObjectManagerProxy::set_interfaces_removed_callback(
+ const InterfacesRemovedSignalCallback &callback) {
+ proxy_.set_interfaces_removed_callback(callback);
+}
+
+// Inherited from DBusObjectManagerProxyInterface.
+DBusObjectManagerProxy::Proxy::Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+DBusObjectManagerProxy::Proxy::~Proxy() {}
+
+void DBusObjectManagerProxy::Proxy::set_interfaces_added_callback(
+ const InterfacesAddedSignalCallback &callback) {
+ interfaces_added_callback_ = callback;
+}
+
+void DBusObjectManagerProxy::Proxy::set_interfaces_removed_callback(
+ const InterfacesRemovedSignalCallback &callback) {
+ interfaces_removed_callback_ = callback;
+}
+
+// Signal callback
+void DBusObjectManagerProxy::Proxy::InterfacesAdded(
+ const ::DBus::Path &object_path,
+ const DBusInterfaceToProperties &interface_to_properties) {
+ SLOG(&path(), 2) << __func__ << "(" << object_path << ")";
+ interfaces_added_callback_.Run(object_path, interface_to_properties);
+}
+
+// Signal callback
+void DBusObjectManagerProxy::Proxy::InterfacesRemoved(
+ const ::DBus::Path &object_path,
+ const std::vector<std::string> &interfaces) {
+ SLOG(&path(), 2) << __func__ << "(" << object_path << ")";
+ interfaces_removed_callback_.Run(object_path, interfaces);
+}
+
+// Method callback
+void DBusObjectManagerProxy::Proxy::GetManagedObjectsCallback(
+ const DBusObjectsWithProperties &objects_with_properties,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ shill::Error error;
+ CellularError::FromDBusError(dberror, &error);
+ std::unique_ptr<ManagedObjectsCallback> callback(
+ reinterpret_cast<ManagedObjectsCallback *>(data));
+
+ callback->Run(objects_with_properties, error);
+}
+
+} // namespace shill
diff --git a/cellular/dbus_objectmanager_proxy.h b/cellular/dbus_objectmanager_proxy.h
new file mode 100644
index 0000000..13f7cde
--- /dev/null
+++ b/cellular/dbus_objectmanager_proxy.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_DBUS_OBJECTMANAGER_PROXY_H_
+#define SHILL_CELLULAR_DBUS_OBJECTMANAGER_PROXY_H_
+
+#include <string>
+#include <vector>
+
+#include "shill/cellular/dbus_objectmanager_proxy_interface.h"
+#include "shill/dbus_proxies/dbus-objectmanager.h"
+
+namespace shill {
+
+class DBusObjectManagerProxy : public DBusObjectManagerProxyInterface {
+ public:
+ // Constructs a org.freedesktop.DBus.ObjectManager DBus object proxy
+ // at |path| owned by |service|.
+
+ DBusObjectManagerProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~DBusObjectManagerProxy() override;
+
+ // Inherited methods from DBusObjectManagerProxyInterface.
+ virtual void GetManagedObjects(Error *error,
+ const ManagedObjectsCallback &callback,
+ int timeout);
+
+ virtual void set_interfaces_added_callback(
+ const InterfacesAddedSignalCallback &callback);
+ virtual void set_interfaces_removed_callback(
+ const InterfacesRemovedSignalCallback &callback);
+
+ private:
+ class Proxy : public org::freedesktop::DBus::ObjectManager_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ virtual void set_interfaces_added_callback(
+ const InterfacesAddedSignalCallback &callback);
+ virtual void set_interfaces_removed_callback(
+ const InterfacesRemovedSignalCallback &callback);
+
+ private:
+ // Signal callbacks
+ virtual void InterfacesAdded(
+ const ::DBus::Path &object_path,
+ const DBusInterfaceToProperties &interfaces_and_properties);
+ virtual void InterfacesRemoved(const ::DBus::Path &object_path,
+ const std::vector<std::string> &interfaces);
+
+ // Method callbacks
+ virtual void GetManagedObjectsCallback(
+ const DBusObjectsWithProperties &objects_with_properties,
+ const DBus::Error &error,
+ void *call_handler);
+
+ InterfacesAddedSignalCallback interfaces_added_callback_;
+ InterfacesRemovedSignalCallback interfaces_removed_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(DBusObjectManagerProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_DBUS_OBJECTMANAGER_PROXY_H_
diff --git a/cellular/dbus_objectmanager_proxy_interface.h b/cellular/dbus_objectmanager_proxy_interface.h
new file mode 100644
index 0000000..ebde700
--- /dev/null
+++ b/cellular/dbus_objectmanager_proxy_interface.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_DBUS_OBJECTMANAGER_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_DBUS_OBJECTMANAGER_PROXY_INTERFACE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/callback.h>
+
+#include "shill/dbus_properties.h" // For DBusPropertiesMap
+
+namespace shill {
+
+class Error;
+
+typedef std::map<std::string, DBusPropertiesMap> DBusInterfaceToProperties;
+typedef std::map<::DBus::Path, DBusInterfaceToProperties>
+ DBusObjectsWithProperties;
+typedef base::Callback<void(const DBusObjectsWithProperties &, const Error &)>
+ ManagedObjectsCallback;
+typedef base::Callback<void(const DBusInterfaceToProperties &, const Error &)>
+ InterfaceAndPropertiesCallback;
+typedef base::Callback<void(const DBus::Path &,
+ const DBusInterfaceToProperties &)>
+ InterfacesAddedSignalCallback;
+typedef base::Callback<void(const DBus::Path &,
+ const std::vector<std::string> &)>
+ InterfacesRemovedSignalCallback;
+
+// These are the methods that a org.freedesktop.DBus.ObjectManager
+// proxy must support. The interface is provided so that it can be
+// mocked in tests. All calls are made asynchronously. Call completion
+// is signalled via the callbacks passed to the methods.
+class DBusObjectManagerProxyInterface {
+ public:
+ virtual ~DBusObjectManagerProxyInterface() {}
+ virtual void GetManagedObjects(Error *error,
+ const ManagedObjectsCallback &callback,
+ int timeout) = 0;
+ virtual void set_interfaces_added_callback(
+ const InterfacesAddedSignalCallback &callback) = 0;
+ virtual void set_interfaces_removed_callback(
+ const InterfacesRemovedSignalCallback &callback) = 0;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_DBUS_OBJECTMANAGER_PROXY_INTERFACE_H_
diff --git a/cellular/mm1_bearer_proxy.cc b/cellular/mm1_bearer_proxy.cc
new file mode 100644
index 0000000..22da205
--- /dev/null
+++ b/cellular/mm1_bearer_proxy.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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/mm1_bearer_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+namespace mm1 {
+
+BearerProxy::BearerProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service)
+ : proxy_(connection, path, service) {}
+
+BearerProxy::~BearerProxy() {}
+
+
+// Inherited methods from BearerProxyInterface
+void BearerProxy::Connect(Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::ConnectAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout);
+}
+
+void BearerProxy::Disconnect(Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::DisconnectAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout);
+}
+
+BearerProxy::Proxy::Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+BearerProxy::Proxy::~Proxy() {}
+
+// Method callbacks inherited from
+// org::freedesktop::ModemManager1::BearerProxy
+void BearerProxy::Proxy::ConnectCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void BearerProxy::Proxy::DisconnectCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mm1_bearer_proxy.h b/cellular/mm1_bearer_proxy.h
new file mode 100644
index 0000000..bdddf13
--- /dev/null
+++ b/cellular/mm1_bearer_proxy.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_BEARER_PROXY_H_
+#define SHILL_CELLULAR_MM1_BEARER_PROXY_H_
+
+#include <string>
+
+#include "dbus_proxies/org.freedesktop.ModemManager1.Bearer.h"
+#include "shill/cellular/mm1_bearer_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+namespace mm1 {
+
+// A proxy to org.freedesktop.ModemManager1.Bearer.
+class BearerProxy : public BearerProxyInterface {
+ public:
+ // Constructs an org.freedesktop.ModemManager1.Bearer DBus object
+ // proxy at |path| owned by |service|.
+ BearerProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+
+ ~BearerProxy() override;
+
+ // Inherited methods from BearerProxyInterface
+ virtual void Connect(Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void Disconnect(Error *error,
+ const ResultCallback &callback,
+ int timeout);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager1::Bearer_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Method callbacks inherited from
+ // org::freedesktop::ModemManager1::BearerProxy
+ virtual void ConnectCallback(const ::DBus::Error &dberror,
+ void *data);
+ virtual void DisconnectCallback(const ::DBus::Error &dberror,
+ void *data);
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(BearerProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_BEARER_PROXY_H_
diff --git a/cellular/mm1_bearer_proxy_interface.h b/cellular/mm1_bearer_proxy_interface.h
new file mode 100644
index 0000000..b51d8db
--- /dev/null
+++ b/cellular/mm1_bearer_proxy_interface.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_BEARER_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MM1_BEARER_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+
+class Error;
+
+namespace mm1 {
+
+// These are the methods that an org.freedesktop.ModemManager1.Bearer
+// proxy must support. The interface is provided so that it can be mocked
+// in tests. All calls are made asynchronously. Call completion is signalled
+// via callbacks passed to the methods.
+class BearerProxyInterface {
+ public:
+ virtual ~BearerProxyInterface() {}
+
+ virtual void Connect(Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void Disconnect(Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_BEARER_PROXY_INTERFACE_H_
diff --git a/cellular/mm1_modem_location_proxy.cc b/cellular/mm1_modem_location_proxy.cc
new file mode 100644
index 0000000..d84aabf
--- /dev/null
+++ b/cellular/mm1_modem_location_proxy.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 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/mm1_modem_location_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+namespace mm1 {
+
+ModemLocationProxy::ModemLocationProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemLocationProxy::~ModemLocationProxy() {}
+
+void ModemLocationProxy::Setup(uint32_t sources,
+ bool signal_location,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::SetupAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout,
+ sources, signal_location);
+}
+
+void ModemLocationProxy::GetLocation(Error *error,
+ const DBusEnumValueMapCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetLocationAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout);
+}
+
+ModemLocationProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemLocationProxy::Proxy::~Proxy() {}
+
+// Method callbacks inherited from
+// org::freedesktop::ModemManager1::Modem:LocationProxy
+void ModemLocationProxy::Proxy::SetupCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemLocationProxy::Proxy::GetLocationCallback(
+ const DBusEnumValueMap &location,
+ const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<DBusEnumValueMapCallback> callback(
+ reinterpret_cast<DBusEnumValueMapCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(location, error);
+}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mm1_modem_location_proxy.h b/cellular/mm1_modem_location_proxy.h
new file mode 100644
index 0000000..16d5be0
--- /dev/null
+++ b/cellular/mm1_modem_location_proxy.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_LOCATION_PROXY_H_
+#define SHILL_CELLULAR_MM1_MODEM_LOCATION_PROXY_H_
+
+#include <string>
+
+#include "dbus_proxies/org.freedesktop.ModemManager1.Modem.Location.h"
+#include "shill/cellular/mm1_modem_location_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+namespace mm1 {
+
+// A proxy to org.freedesktop.ModemManager1.Bearer.
+class ModemLocationProxy : public ModemLocationProxyInterface {
+ public:
+ // Constructs an org.freedesktop.ModemManager1.Modem.Location DBus
+ // object proxy at |path| owned by |service|.
+ ModemLocationProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+
+ ~ModemLocationProxy() override;
+
+ // Inherited methods from ModemLocationProxyInterface.
+ virtual void Setup(uint32_t sources,
+ bool signal_location,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+
+ virtual void GetLocation(Error *error,
+ const DBusEnumValueMapCallback &callback,
+ int timeout);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager1::Modem::Location_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Method callbacks inherited from
+ // org::freedesktop::ModemManager1::Modem:Location_proxy
+ virtual void SetupCallback(const ::DBus::Error &dberror,
+ void *data);
+
+ virtual void GetLocationCallback(const DBusEnumValueMap &location,
+ const ::DBus::Error &dberror,
+ void *data);
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemLocationProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_LOCATION_PROXY_H_
diff --git a/cellular/mm1_modem_location_proxy_interface.h b/cellular/mm1_modem_location_proxy_interface.h
new file mode 100644
index 0000000..12aaba3
--- /dev/null
+++ b/cellular/mm1_modem_location_proxy_interface.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_LOCATION_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MM1_MODEM_LOCATION_PROXY_INTERFACE_H_
+
+#include "shill/callbacks.h"
+
+namespace shill {
+class Error;
+
+namespace mm1 {
+
+// These are the methods that an org.freedesktop.ModemManager1.Modem.Location
+// proxy must support. The interface is provided so that it can be mocked
+// in tests. All calls are made asynchronously. Call completion is signalled
+// via callbacks passed to the methods.
+class ModemLocationProxyInterface {
+ public:
+ virtual ~ModemLocationProxyInterface() {}
+
+ virtual void Setup(uint32_t sources,
+ bool signal_location,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+
+ virtual void GetLocation(Error *error,
+ const DBusEnumValueMapCallback &callback,
+ int timeout) = 0;
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_LOCATION_PROXY_INTERFACE_H_
diff --git a/cellular/mm1_modem_modem3gpp_proxy.cc b/cellular/mm1_modem_modem3gpp_proxy.cc
new file mode 100644
index 0000000..3673f4a
--- /dev/null
+++ b/cellular/mm1_modem_modem3gpp_proxy.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 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/mm1_modem_modem3gpp_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+namespace mm1 {
+
+ModemModem3gppProxy::ModemModem3gppProxy(
+ DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemModem3gppProxy::~ModemModem3gppProxy() {}
+
+void ModemModem3gppProxy::Register(const std::string &operator_id,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::RegisterAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout,
+ operator_id);
+}
+
+void ModemModem3gppProxy::Scan(Error *error,
+ const DBusPropertyMapsCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::ScanAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout);
+}
+
+// ModemModem3gppProxy::Proxy
+ModemModem3gppProxy::Proxy::Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemModem3gppProxy::Proxy::~Proxy() {}
+
+// Method callbacks inherited from
+// org::freedesktop::ModemManager1::Modem::ModemModem3gppProxy
+void ModemModem3gppProxy::Proxy::RegisterCallback(const ::DBus::Error& dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemModem3gppProxy::Proxy::ScanCallback(
+ const std::vector<DBusPropertiesMap> &results,
+ const ::DBus::Error& dberror, void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<DBusPropertyMapsCallback> callback(
+ reinterpret_cast<DBusPropertyMapsCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(results, error);
+}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mm1_modem_modem3gpp_proxy.h b/cellular/mm1_modem_modem3gpp_proxy.h
new file mode 100644
index 0000000..8b7de1f
--- /dev/null
+++ b/cellular/mm1_modem_modem3gpp_proxy.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_MODEM3GPP_PROXY_H_
+#define SHILL_CELLULAR_MM1_MODEM_MODEM3GPP_PROXY_H_
+
+#include <string>
+#include <vector>
+
+#include "dbus_proxies/org.freedesktop.ModemManager1.Modem.Modem3gpp.h"
+#include "shill/cellular/mm1_modem_modem3gpp_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+namespace mm1 {
+
+// A proxy to org.freedesktop.ModemManager1.Modem.Modem3gpp.
+class ModemModem3gppProxy : public ModemModem3gppProxyInterface {
+ public:
+ // Constructs an org.freedesktop.ModemManager1.Modem.Modem3gpp DBus
+ // object proxy at |path| owned by |service|.
+ ModemModem3gppProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemModem3gppProxy() override;
+ // Inherited methods from ModemModem3gppProxyInterface.
+ virtual void Register(const std::string &operator_id,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void Scan(Error *error,
+ const DBusPropertyMapsCallback &callback,
+ int timeout);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager1::Modem::Modem3gpp_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Method callbacks inherited from
+ // org::freedesktop::ModemManager1::Modem::Modem3gppProxy
+ virtual void RegisterCallback(const ::DBus::Error& dberror, void *data);
+ virtual void ScanCallback(
+ const std::vector<DBusPropertiesMap> &results,
+ const ::DBus::Error& dberror, void *data);
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemModem3gppProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_MODEM3GPP_PROXY_H_
diff --git a/cellular/mm1_modem_modem3gpp_proxy_interface.h b/cellular/mm1_modem_modem3gpp_proxy_interface.h
new file mode 100644
index 0000000..10a8508
--- /dev/null
+++ b/cellular/mm1_modem_modem3gpp_proxy_interface.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_MODEM3GPP_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MM1_MODEM_MODEM3GPP_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+class Error;
+
+namespace mm1 {
+
+// These are the methods that a
+// org.freedesktop.ModemManager1.Modem.Modem3gpp proxy must support.
+// The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously. Call completion is signalled via
+// the callbacks passed to the methods.
+class ModemModem3gppProxyInterface {
+ public:
+ virtual ~ModemModem3gppProxyInterface() {}
+
+ virtual void Register(const std::string &operator_id,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void Scan(Error *error,
+ const DBusPropertyMapsCallback &callback,
+ int timeout) = 0;
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_MODEM3GPP_PROXY_INTERFACE_H_
diff --git a/cellular/mm1_modem_modemcdma_proxy.cc b/cellular/mm1_modem_modemcdma_proxy.cc
new file mode 100644
index 0000000..2a8f59e
--- /dev/null
+++ b/cellular/mm1_modem_modemcdma_proxy.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2012 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/mm1_modem_modemcdma_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+namespace mm1 {
+
+ModemModemCdmaProxy::ModemModemCdmaProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemModemCdmaProxy::~ModemModemCdmaProxy() {}
+
+void ModemModemCdmaProxy::Activate(const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::ActivateAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout,
+ carrier);
+}
+
+void ModemModemCdmaProxy::ActivateManual(
+ const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::ActivateManualAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout,
+ properties);
+}
+
+void ModemModemCdmaProxy::set_activation_state_callback(
+ const ActivationStateSignalCallback &callback) {
+ proxy_.set_activation_state_callback(callback);
+}
+
+// ModemModemCdmaProxy::Proxy
+ModemModemCdmaProxy::Proxy::Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemModemCdmaProxy::Proxy::~Proxy() {}
+
+void ModemModemCdmaProxy::Proxy::set_activation_state_callback(
+ const ActivationStateSignalCallback &callback) {
+ activation_state_callback_ = callback;
+}
+
+// Signal callbacks inherited from Proxy
+void ModemModemCdmaProxy::Proxy::ActivationStateChanged(
+ const uint32_t &activation_state,
+ const uint32_t &activation_error,
+ const DBusPropertiesMap &status_changes) {
+ SLOG(&path(), 2) << __func__;
+ activation_state_callback_.Run(activation_state,
+ activation_error,
+ status_changes);
+}
+
+// Method callbacks inherited from
+// org::freedesktop::ModemManager1::Modem::ModemModemCdmaProxy
+void ModemModemCdmaProxy::Proxy::ActivateCallback(const ::DBus::Error& dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemModemCdmaProxy::Proxy::ActivateManualCallback(
+ const ::DBus::Error& dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mm1_modem_modemcdma_proxy.h b/cellular/mm1_modem_modemcdma_proxy.h
new file mode 100644
index 0000000..d1ae73c
--- /dev/null
+++ b/cellular/mm1_modem_modemcdma_proxy.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_MODEMCDMA_PROXY_H_
+#define SHILL_CELLULAR_MM1_MODEM_MODEMCDMA_PROXY_H_
+
+#include <string>
+
+#include "dbus_proxies/org.freedesktop.ModemManager1.Modem.ModemCdma.h"
+#include "shill/cellular/mm1_modem_modemcdma_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+namespace mm1 {
+
+// A proxy to org.freedesktop.ModemManager1.Modem.ModemCdma.
+class ModemModemCdmaProxy : public ModemModemCdmaProxyInterface {
+ public:
+ // Constructs a org.freedesktop.ModemManager1.Modem.ModemCdma DBus
+ // object proxy at |path| owned by |service|.
+ ModemModemCdmaProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemModemCdmaProxy() override;
+
+ // Inherited methods from ModemModemCdmaProxyInterface.
+ virtual void Activate(const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void ActivateManual(
+ const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+
+ virtual void set_activation_state_callback(
+ const ActivationStateSignalCallback &callback);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager1::Modem::ModemCdma_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ virtual void set_activation_state_callback(
+ const ActivationStateSignalCallback &callback);
+
+ private:
+ // Signal callbacks inherited from Proxy
+ // handle signals
+ void ActivationStateChanged(
+ const uint32_t &activation_state,
+ const uint32_t &activation_error,
+ const DBusPropertiesMap &status_changes);
+
+ // Method callbacks inherited from
+ // org::freedesktop::ModemManager1::Modem::ModemCdmaProxy
+ virtual void ActivateCallback(const ::DBus::Error& dberror, void *data);
+ virtual void ActivateManualCallback(const ::DBus::Error& dberror,
+ void *data);
+ ActivationStateSignalCallback activation_state_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemModemCdmaProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_MODEMCDMA_PROXY_H_
diff --git a/cellular/mm1_modem_modemcdma_proxy_interface.h b/cellular/mm1_modem_modemcdma_proxy_interface.h
new file mode 100644
index 0000000..6fe7fcc
--- /dev/null
+++ b/cellular/mm1_modem_modemcdma_proxy_interface.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_MODEMCDMA_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MM1_MODEM_MODEMCDMA_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+class Error;
+
+namespace mm1 {
+
+// These are the methods that a
+// org.freedesktop.ModemManager1.Modem.ModemCdma proxy must support.
+// The interface is provided so that it can be mocked in tests. All
+// calls are made asynchronously. Call completion is signalled via
+// the callbacks passed to the methods.
+class ModemModemCdmaProxyInterface {
+ public:
+ virtual ~ModemModemCdmaProxyInterface() {}
+
+ virtual void Activate(const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void ActivateManual(
+ const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+
+ virtual void set_activation_state_callback(
+ const ActivationStateSignalCallback &callback) = 0;
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_MODEMCDMA_PROXY_INTERFACE_H_
diff --git a/cellular/mm1_modem_proxy.cc b/cellular/mm1_modem_proxy.cc
new file mode 100644
index 0000000..25d62f7
--- /dev/null
+++ b/cellular/mm1_modem_proxy.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2012 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/mm1_modem_proxy.h"
+
+#include <ModemManager/ModemManager.h>
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+namespace mm1 {
+
+template<typename TraceMsgT, typename CallT, typename CallbackT,
+ typename... ArgTypes>
+void ModemProxy::BeginCall(
+ const TraceMsgT &trace_msg, const CallT &call, const CallbackT &callback,
+ Error *error, int timeout, ArgTypes... rest) {
+ BeginAsyncDBusCall(trace_msg, proxy_, call, callback, error,
+ &CellularError::FromMM1DBusError, timeout, rest...);
+}
+
+ModemProxy::ModemProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemProxy::~ModemProxy() {}
+
+void ModemProxy::set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback) {
+ proxy_.set_state_changed_callback(callback);
+}
+
+void ModemProxy::Enable(bool enable,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ SLOG(&proxy_.path(), 2) << __func__ << "(" << enable << ", "
+ << timeout << ")";
+ BeginCall(__func__, &Proxy::EnableAsync, callback, error, timeout,
+ enable);
+}
+
+void ModemProxy::CreateBearer(
+ const DBusPropertiesMap &properties,
+ Error *error,
+ const DBusPathCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::CreateBearerAsync, callback, error, timeout,
+ properties);
+}
+
+void ModemProxy::DeleteBearer(const ::DBus::Path &bearer,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::DeleteBearerAsync, callback, error, timeout,
+ bearer);
+}
+
+void ModemProxy::Reset(Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::ResetAsync, callback, error, timeout);
+}
+
+void ModemProxy::FactoryReset(const std::string &code,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::FactoryResetAsync, callback, error, timeout,
+ code);
+}
+
+void ModemProxy::SetCurrentCapabilities(const uint32_t &capabilities,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::SetCurrentCapabilitiesAsync, callback, error,
+ timeout, capabilities);
+}
+
+void ModemProxy::SetCurrentModes(
+ const ::DBus::Struct<uint32_t, uint32_t> &modes,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::SetCurrentModesAsync, callback, error, timeout,
+ modes);
+}
+
+void ModemProxy::SetCurrentBands(const std::vector<uint32_t> &bands,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::SetCurrentBandsAsync, callback, error, timeout,
+ bands);
+}
+
+void ModemProxy::Command(const std::string &cmd,
+ const uint32_t &user_timeout,
+ Error *error,
+ const StringCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::CommandAsync, callback, error, timeout,
+ cmd, user_timeout);
+}
+
+void ModemProxy::SetPowerState(const uint32_t &power_state,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::SetPowerStateAsync, callback, error, timeout,
+ power_state);
+}
+
+ModemProxy::Proxy::Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemProxy::Proxy::~Proxy() {}
+
+void ModemProxy::Proxy::set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback) {
+ state_changed_callback_ = callback;
+}
+
+// Signal callbacks inherited from Proxy
+void ModemProxy::Proxy::StateChanged(const int32_t &old,
+ const int32_t &_new,
+ const uint32_t &reason) {
+ SLOG(DBus, &path(), 2) << __func__;
+ if (!state_changed_callback_.is_null())
+ state_changed_callback_.Run(old, _new, reason);
+}
+
+// Method callbacks inherited from
+// org::freedesktop::ModemManager1::ModemProxy
+void ModemProxy::Proxy::EnableCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::CreateBearerCallback(const ::DBus::Path &path,
+ const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path, 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::DeleteBearerCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::ResetCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::FactoryResetCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::SetCurrentCapabilitesCallback(
+ const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::SetCurrentModesCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::SetCurrentBandsCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::CommandCallback(const std::string &response,
+ const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::SetPowerStateCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mm1_modem_proxy.h b/cellular/mm1_modem_proxy.h
new file mode 100644
index 0000000..d4590ed
--- /dev/null
+++ b/cellular/mm1_modem_proxy.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_PROXY_H_
+#define SHILL_CELLULAR_MM1_MODEM_PROXY_H_
+
+#include <string>
+#include <vector>
+
+#include "dbus_proxies/org.freedesktop.ModemManager1.Modem.h"
+#include "shill/cellular/mm1_modem_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+namespace mm1 {
+
+// A proxy to org.freedesktop.ModemManager1.Modem.
+class ModemProxy : public ModemProxyInterface {
+ public:
+ // Constructs a org.freedesktop.ModemManager1.Modem DBus object
+ // proxy at |path| owned by |service|.
+ ModemProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemProxy() override;
+
+ // Inherited methods from ModemProxyInterface.
+ virtual void Enable(bool enable,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void CreateBearer(const DBusPropertiesMap &properties,
+ Error *error,
+ const DBusPathCallback &callback,
+ int timeout);
+ virtual void DeleteBearer(const ::DBus::Path &bearer,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void Reset(Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void FactoryReset(const std::string &code,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void SetCurrentCapabilities(const uint32_t &capabilities,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void SetCurrentModes(const ::DBus::Struct<uint32_t, uint32_t> &modes,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void SetCurrentBands(const std::vector<uint32_t> &bands,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void Command(const std::string &cmd,
+ const uint32_t &user_timeout,
+ Error *error,
+ const StringCallback &callback,
+ int timeout);
+ virtual void SetPowerState(const uint32_t &power_state,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+
+ virtual void set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager1::Modem_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ void set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback);
+
+ private:
+ // Signal callbacks inherited from Proxy
+ // handle signals
+ void StateChanged(const int32_t &old,
+ const int32_t &_new,
+ const uint32_t &reason);
+
+ // Method callbacks inherited from
+ // org::freedesktop::ModemManager1::ModemProxy
+ virtual void EnableCallback(const ::DBus::Error &dberror, void *data);
+ virtual void CreateBearerCallback(const ::DBus::Path &bearer,
+ const ::DBus::Error &dberror, void *data);
+ virtual void DeleteBearerCallback(const ::DBus::Error &dberror, void *data);
+ virtual void ResetCallback(const ::DBus::Error &dberror, void *data);
+ virtual void FactoryResetCallback(const ::DBus::Error &dberror, void *data);
+ virtual void SetCurrentCapabilitesCallback(const ::DBus::Error &dberror,
+ void *data);
+ virtual void SetCurrentModesCallback(const ::DBus::Error &dberror,
+ void *data);
+ virtual void SetCurrentBandsCallback(const ::DBus::Error &dberror,
+ void *data);
+ virtual void CommandCallback(const std::string &response,
+ const ::DBus::Error &dberror,
+ void *data);
+ virtual void SetPowerStateCallback(const ::DBus::Error &dberror,
+ void *data);
+
+ ModemStateChangedSignalCallback state_changed_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ template<typename TraceMsgT, typename CallT, typename CallbackT,
+ typename... ArgTypes>
+ void BeginCall(
+ const TraceMsgT &trace_msg, const CallT &call,
+ const CallbackT &callback, Error *error, int timeout,
+ ArgTypes... rest);
+
+ DISALLOW_COPY_AND_ASSIGN(ModemProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_PROXY_H_
diff --git a/cellular/mm1_modem_proxy_interface.h b/cellular/mm1_modem_proxy_interface.h
new file mode 100644
index 0000000..cf76978
--- /dev/null
+++ b/cellular/mm1_modem_proxy_interface.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MM1_MODEM_PROXY_INTERFACE_H_
+
+#include <string>
+#include <vector>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+class Error;
+
+namespace mm1 {
+
+typedef base::Callback<void(int32_t,
+ int32_t, uint32_t)> ModemStateChangedSignalCallback;
+
+// These are the methods that a org.freedesktop.ModemManager1.Modem
+// proxy must support. The interface is provided so that it can be
+// mocked in tests. All calls are made asynchronously. Call completion
+// is signalled via the callbacks passed to the methods.
+class ModemProxyInterface {
+ public:
+ virtual ~ModemProxyInterface() {}
+
+ virtual void Enable(bool enable,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void CreateBearer(const DBusPropertiesMap &properties,
+ Error *error,
+ const DBusPathCallback &callback,
+ int timeout) = 0;
+ virtual void DeleteBearer(const ::DBus::Path &bearer,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void Reset(Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void FactoryReset(const std::string &code,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void SetCurrentCapabilities(const uint32_t &capabilities,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void SetCurrentModes(const ::DBus::Struct<uint32_t, uint32_t> &modes,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void SetCurrentBands(const std::vector<uint32_t> &bands,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void Command(const std::string &cmd,
+ const uint32_t &user_timeout,
+ Error *error,
+ const StringCallback &callback,
+ int timeout) = 0;
+ virtual void SetPowerState(const uint32_t &power_state,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+
+
+ virtual void set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback) = 0;
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_PROXY_INTERFACE_H_
diff --git a/cellular/mm1_modem_simple_proxy.cc b/cellular/mm1_modem_simple_proxy.cc
new file mode 100644
index 0000000..cb42a03
--- /dev/null
+++ b/cellular/mm1_modem_simple_proxy.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2012 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/mm1_modem_simple_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+namespace mm1 {
+
+ModemSimpleProxy::ModemSimpleProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemSimpleProxy::~ModemSimpleProxy() {}
+
+void ModemSimpleProxy::Connect(
+ const DBusPropertiesMap &properties,
+ Error *error,
+ const DBusPathCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::ConnectAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout,
+ properties);
+}
+
+void ModemSimpleProxy::Disconnect(const ::DBus::Path &bearer,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::DisconnectAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout,
+ bearer);
+}
+
+void ModemSimpleProxy::GetStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetStatusAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout);
+}
+
+
+// ModemSimpleProxy::Proxy
+ModemSimpleProxy::Proxy::Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemSimpleProxy::Proxy::~Proxy() {}
+
+// Method callbacks inherited from
+// org::freedesktop::ModemManager1::Modem::ModemSimpleProxy
+void ModemSimpleProxy::Proxy::ConnectCallback(const ::DBus::Path &bearer,
+ const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(&bearer, 2) << __func__;
+ unique_ptr<DBusPathCallback> callback(
+ reinterpret_cast<DBusPathCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(bearer, error);
+}
+
+void ModemSimpleProxy::Proxy::DisconnectCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemSimpleProxy::Proxy::GetStatusCallback(
+ const DBusPropertiesMap &properties,
+ const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<DBusPropertyMapCallback> callback(
+ reinterpret_cast<DBusPropertyMapCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(properties, error);
+}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mm1_modem_simple_proxy.h b/cellular/mm1_modem_simple_proxy.h
new file mode 100644
index 0000000..c2b5047
--- /dev/null
+++ b/cellular/mm1_modem_simple_proxy.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_SIMPLE_PROXY_H_
+#define SHILL_CELLULAR_MM1_MODEM_SIMPLE_PROXY_H_
+
+#include <string>
+
+#include "dbus_proxies/org.freedesktop.ModemManager1.Modem.Simple.h"
+#include "shill/cellular/mm1_modem_simple_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+namespace mm1 {
+
+// A proxy to org.freedesktop.ModemManager1.Modem.Simple.
+class ModemSimpleProxy : public ModemSimpleProxyInterface {
+ public:
+ // Constructs a org.freedesktop.ModemManager1.Modem.Simple DBus
+ // object proxy at |path| owned by |service|.
+ ModemSimpleProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemSimpleProxy() override;
+
+ // Inherited methods from SimpleProxyInterface.
+ virtual void Connect(
+ const DBusPropertiesMap &properties,
+ Error *error,
+ const DBusPathCallback &callback,
+ int timeout);
+ virtual void Disconnect(const ::DBus::Path &bearer,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void GetStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager1::Modem::Simple_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Method callbacks inherited from
+ // org::freedesktop::ModemManager1::Modem::SimpleProxy
+ virtual void ConnectCallback(const ::DBus::Path &bearer,
+ const ::DBus::Error &dberror,
+ void *data);
+ virtual void DisconnectCallback(const ::DBus::Error &dberror, void *data);
+ virtual void GetStatusCallback(
+ const DBusPropertiesMap &bearer,
+ const ::DBus::Error &dberror,
+ void *data);
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemSimpleProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_SIMPLE_PROXY_H_
diff --git a/cellular/mm1_modem_simple_proxy_interface.h b/cellular/mm1_modem_simple_proxy_interface.h
new file mode 100644
index 0000000..8ba256b
--- /dev/null
+++ b/cellular/mm1_modem_simple_proxy_interface.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_SIMPLE_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MM1_MODEM_SIMPLE_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+class Error;
+
+namespace mm1 {
+
+// These are the methods that a
+// org.freedesktop.ModemManager1.Modem.Simple proxy must support. The
+// interface is provided so that it can be mocked in tests. All calls
+// are made asynchronously. Call completion is signalled via the callbacks
+// passed to the methods.
+class ModemSimpleProxyInterface {
+ public:
+ virtual ~ModemSimpleProxyInterface() {}
+
+ virtual void Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const DBusPathCallback &callback,
+ int timeout) = 0;
+ virtual void Disconnect(const ::DBus::Path &bearer,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void GetStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout) = 0;
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_SIMPLE_PROXY_INTERFACE_H_
diff --git a/cellular/mm1_modem_time_proxy.cc b/cellular/mm1_modem_time_proxy.cc
new file mode 100644
index 0000000..2ba2e1e
--- /dev/null
+++ b/cellular/mm1_modem_time_proxy.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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/mm1_modem_time_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+namespace mm1 {
+
+ModemTimeProxy::ModemTimeProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemTimeProxy::~ModemTimeProxy() {}
+
+void ModemTimeProxy::set_network_time_changed_callback(
+ const NetworkTimeChangedSignalCallback &callback) {
+ proxy_.set_network_time_changed_callback(callback);
+}
+
+void ModemTimeProxy::GetNetworkTime(Error *error,
+ const StringCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetNetworkTimeAsync, callback,
+ error, &CellularError::FromMM1DBusError, timeout);
+}
+
+ModemTimeProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemTimeProxy::Proxy::~Proxy() {}
+
+void ModemTimeProxy::Proxy::set_network_time_changed_callback(
+ const NetworkTimeChangedSignalCallback &callback) {
+ network_time_changed_callback_ = callback;
+}
+
+// Signal callbacks inherited from Proxy
+void ModemTimeProxy::Proxy::NetworkTimeChanged(const string &time) {
+ SLOG(&path(), 2) << __func__;
+ if (!network_time_changed_callback_.is_null())
+ network_time_changed_callback_.Run(time);
+}
+
+// Method callbacks inherited from
+// org::freedesktop::ModemManager1::Modem::TimeProxy
+void ModemTimeProxy::Proxy::GetNetworkTimeCallback(const string &time,
+ const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<StringCallback> callback(reinterpret_cast<StringCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(time, error);
+}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mm1_modem_time_proxy.h b/cellular/mm1_modem_time_proxy.h
new file mode 100644
index 0000000..36cf07b
--- /dev/null
+++ b/cellular/mm1_modem_time_proxy.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_TIME_PROXY_H_
+#define SHILL_CELLULAR_MM1_MODEM_TIME_PROXY_H_
+
+#include <string>
+
+#include "dbus_proxies/org.freedesktop.ModemManager1.Modem.Time.h"
+#include "shill/cellular/mm1_modem_time_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+namespace mm1 {
+
+// A proxy to org.freedesktop.ModemManager1.Modem.Time.
+class ModemTimeProxy : public ModemTimeProxyInterface {
+ public:
+ // Constructs an org.freedesktop.ModemManager1.Modem.Time DBus object
+ // proxy at |path| owned by |service|.
+ ModemTimeProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemTimeProxy() override;
+
+ // Inherited methods from ModemTimeProxyInterface.
+ virtual void GetNetworkTime(Error *error,
+ const StringCallback &callback,
+ int timeout);
+
+ virtual void set_network_time_changed_callback(
+ const NetworkTimeChangedSignalCallback &callback);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager1::Modem::Time_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ void set_network_time_changed_callback(
+ const NetworkTimeChangedSignalCallback &callback);
+
+ private:
+ // Signal callbacks inherited from Proxy
+ // handle signals.
+ void NetworkTimeChanged(const std::string &time);
+
+ // Method callbacks inherited from
+ // org::freedesktop::ModemManager1::Modem::Time_proxy.
+ virtual void GetNetworkTimeCallback(const std::string &time,
+ const ::DBus::Error &dberror,
+ void *data);
+
+ NetworkTimeChangedSignalCallback network_time_changed_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemTimeProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_TIME_PROXY_H_
diff --git a/cellular/mm1_modem_time_proxy_interface.h b/cellular/mm1_modem_time_proxy_interface.h
new file mode 100644
index 0000000..31e84b1
--- /dev/null
+++ b/cellular/mm1_modem_time_proxy_interface.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_MODEM_TIME_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MM1_MODEM_TIME_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+class Error;
+
+namespace mm1 {
+
+typedef base::Callback<void(const std::string &)>
+ NetworkTimeChangedSignalCallback;
+
+// These are the methods that an org.freedesktop.ModemManager1.Modem.Time
+// proxy must support. The interface is provided so that it can be mocked
+// in tests. All calls are made asynchronously. Call completion is signalled
+// via callbacks passed to the methods.
+class ModemTimeProxyInterface {
+ public:
+ virtual ~ModemTimeProxyInterface() {}
+
+ virtual void GetNetworkTime(Error *error,
+ const StringCallback &callback,
+ int timeout) = 0;
+
+ virtual void set_network_time_changed_callback(
+ const NetworkTimeChangedSignalCallback &callback) = 0;
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_MODEM_TIME_PROXY_INTERFACE_H_
diff --git a/cellular/mm1_sim_proxy.cc b/cellular/mm1_sim_proxy.cc
new file mode 100644
index 0000000..190925d
--- /dev/null
+++ b/cellular/mm1_sim_proxy.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2012 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/mm1_sim_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+namespace mm1 {
+
+template<typename TraceMsgT, typename CallT, typename CallbackT,
+ typename... ArgTypes>
+void SimProxy::BeginCall(
+ const TraceMsgT &trace_msg, const CallT &call, const CallbackT &callback,
+ Error *error, int timeout, ArgTypes... rest) {
+ BeginAsyncDBusCall(trace_msg, proxy_, call, callback, error,
+ &CellularError::FromMM1DBusError, timeout, rest...);
+}
+
+SimProxy::SimProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+SimProxy::~SimProxy() {}
+
+
+void SimProxy::SendPin(const string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ // pin is intentionally not logged.
+ SLOG(&proxy_.path(), 2) << __func__ << "( XXX, " << timeout << ")";
+ BeginCall(__func__, &Proxy::SendPinAsync, callback, error, timeout,
+ pin);
+}
+
+void SimProxy::SendPuk(const string &puk,
+ const string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ // pin and puk are intentionally not logged.
+ SLOG(&proxy_.path(), 2) << __func__ << "( XXX, XXX, " << timeout << ")";
+ BeginCall(__func__, &Proxy::SendPukAsync, callback, error, timeout,
+ puk, pin);
+}
+
+void SimProxy::EnablePin(const string &pin,
+ const bool enabled,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ // pin is intentionally not logged.
+ SLOG(&proxy_.path(), 2) << __func__ << "( XXX, "
+ << enabled << ", " << timeout << ")";
+ BeginCall(__func__, &Proxy::EnablePinAsync, callback, error, timeout,
+ pin, enabled);
+}
+
+void SimProxy::ChangePin(const string &old_pin,
+ const string &new_pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ // old_pin and new_pin are intentionally not logged.
+ SLOG(&proxy_.path(), 2) << __func__ << "( XXX, XXX, " << timeout << ")";
+ BeginCall(__func__, &Proxy::ChangePinAsync, callback, error, timeout,
+ old_pin, new_pin);
+}
+
+SimProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+SimProxy::Proxy::~Proxy() {}
+
+// Method callbacks inherited from
+// org::freedesktop::ModemManager1::SimProxy
+void SimProxy::Proxy::SendPinCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void SimProxy::Proxy::SendPukCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void SimProxy::Proxy::EnablePinCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void SimProxy::Proxy::ChangePinCallback(const ::DBus::Error &dberror,
+ void *data) {
+ SLOG(DBus, &path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromMM1DBusError(dberror, &error);
+ callback->Run(error);
+}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mm1_sim_proxy.h b/cellular/mm1_sim_proxy.h
new file mode 100644
index 0000000..ce72fd6
--- /dev/null
+++ b/cellular/mm1_sim_proxy.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_SIM_PROXY_H_
+#define SHILL_CELLULAR_MM1_SIM_PROXY_H_
+
+#include <string>
+
+#include "dbus_proxies/org.freedesktop.ModemManager1.Sim.h"
+#include "shill/cellular/mm1_sim_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+namespace mm1 {
+
+// A proxy to org.freedesktop.ModemManager1.Sim.
+class SimProxy : public SimProxyInterface {
+ public:
+ // Constructs an org.freedesktop.ModemManager1.Sim DBus object
+ // proxy at |path| owned by |service|.
+ SimProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~SimProxy() override;
+
+ // Inherited methods from SimProxyInterface.
+ virtual void SendPin(const std::string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void SendPuk(const std::string &puk,
+ const std::string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void EnablePin(const std::string &pin,
+ const bool enabled,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+ virtual void ChangePin(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager1::Sim_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Method callbacks inherited from
+ // org::freedesktop::ModemManager1::SimProxy
+ virtual void SendPinCallback(const ::DBus::Error &dberror,
+ void *data);
+ virtual void SendPukCallback(const ::DBus::Error &dberror,
+ void *data);
+ virtual void EnablePinCallback(const ::DBus::Error &dberror,
+ void *data);
+ virtual void ChangePinCallback(const ::DBus::Error &dberror,
+ void *data);
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ template<typename TraceMsgT, typename CallT, typename CallbackT,
+ typename... ArgTypes>
+ void BeginCall(
+ const TraceMsgT &trace_msg, const CallT &call,
+ const CallbackT &callback, Error *error, int timeout,
+ ArgTypes... rest);
+
+ DISALLOW_COPY_AND_ASSIGN(SimProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_SIM_PROXY_H_
diff --git a/cellular/mm1_sim_proxy_interface.h b/cellular/mm1_sim_proxy_interface.h
new file mode 100644
index 0000000..b7a3769
--- /dev/null
+++ b/cellular/mm1_sim_proxy_interface.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MM1_SIM_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MM1_SIM_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+
+class Error;
+
+namespace mm1 {
+
+// These are the methods that a org.freedesktop.ModemManager1.Sim
+// proxy must support. The interface is provided so that it can be
+// mocked in tests. All calls are made asynchronously. Call completion
+// is signalled via the callbacks passed to the methods.
+class SimProxyInterface {
+ public:
+ virtual ~SimProxyInterface() {}
+
+ virtual void SendPin(const std::string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void SendPuk(const std::string &puk,
+ const std::string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void EnablePin(const std::string &pin,
+ const bool enabled,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void ChangePin(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) = 0;
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MM1_SIM_PROXY_INTERFACE_H_
diff --git a/cellular/mobile_operator_info.cc b/cellular/mobile_operator_info.cc
new file mode 100644
index 0000000..988875c
--- /dev/null
+++ b/cellular/mobile_operator_info.cc
@@ -0,0 +1,253 @@
+// Copyright (c) 2014 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/mobile_operator_info.h"
+
+#include <sstream>
+
+#include "shill/cellular/mobile_operator_info_impl.h"
+#include "shill/logging.h"
+
+namespace shill {
+
+using base::FilePath;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(const MobileOperatorInfo *m) {
+ return "(mobile_operator_info)";
+}
+}
+
+// /////////////////////////////////////////////////////////////////////////////
+// MobileOperatorInfo implementation note:
+// MobileOperatorInfo simply forwards all operations to |impl_|.
+// It also logs the functions/arguments/results at sane log levels. So the
+// implementation need not leave a trace itself.
+
+MobileOperatorInfo::MobileOperatorInfo(EventDispatcher *dispatcher,
+ const string &info_owner)
+ : impl_(new MobileOperatorInfoImpl(dispatcher, info_owner)) {}
+
+MobileOperatorInfo::~MobileOperatorInfo() {}
+
+string MobileOperatorInfo::GetLogPrefix(const char *func) const {
+ return impl_->info_owner() + ": " + func;
+}
+
+void MobileOperatorInfo::ClearDatabasePaths() {
+ SLOG(this, 3) << GetLogPrefix(__func__);
+ impl_->ClearDatabasePaths();
+}
+
+void MobileOperatorInfo::AddDatabasePath(const FilePath &absolute_path) {
+ SLOG(this, 3) << GetLogPrefix(__func__) << "(" << absolute_path.value()
+ << ")";
+ impl_->AddDatabasePath(absolute_path);
+}
+
+bool MobileOperatorInfo::Init() {
+ auto result = impl_->Init();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+void MobileOperatorInfo::AddObserver(MobileOperatorInfo::Observer *observer) {
+ SLOG(this, 3) << GetLogPrefix(__func__);
+ impl_->AddObserver(observer);
+}
+
+void MobileOperatorInfo::RemoveObserver(
+ MobileOperatorInfo::Observer *observer) {
+ SLOG(this, 3) << GetLogPrefix(__func__);
+ impl_->RemoveObserver(observer);
+}
+
+bool MobileOperatorInfo::IsMobileNetworkOperatorKnown() const {
+ auto result = impl_->IsMobileNetworkOperatorKnown();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+bool MobileOperatorInfo::IsMobileVirtualNetworkOperatorKnown() const {
+ auto result = impl_->IsMobileVirtualNetworkOperatorKnown();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+const string &MobileOperatorInfo::uuid() const {
+ const auto &result = impl_->uuid();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+const string &MobileOperatorInfo::operator_name() const {
+ const auto &result = impl_->operator_name();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+const string &MobileOperatorInfo::country() const {
+ const auto &result = impl_->country();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+const string &MobileOperatorInfo::mccmnc() const {
+ const auto &result = impl_->mccmnc();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+const string &MobileOperatorInfo::sid() const {
+ const auto &result = impl_->sid();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+const string &MobileOperatorInfo::nid() const {
+ const auto &result = impl_->nid();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+const vector<string> &MobileOperatorInfo::mccmnc_list() const {
+ const auto &result = impl_->mccmnc_list();
+ if (SLOG_IS_ON(Cellular, 3)) {
+ stringstream pp_result;
+ for (const auto &mccmnc : result) {
+ pp_result << mccmnc << " ";
+ }
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result["
+ << pp_result.str() << "]";
+ }
+ return result;
+}
+
+const vector<string> &MobileOperatorInfo::sid_list() const {
+ const auto &result = impl_->sid_list();
+ if (SLOG_IS_ON(Cellular, 3)) {
+ stringstream pp_result;
+ for (const auto &sid : result) {
+ pp_result << sid << " ";
+ }
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result["
+ << pp_result.str() << "]";
+ }
+ return result;
+}
+
+const vector<MobileOperatorInfo::LocalizedName> &
+MobileOperatorInfo::operator_name_list() const {
+ const auto &result = impl_->operator_name_list();
+ if (SLOG_IS_ON(Cellular, 3)) {
+ stringstream pp_result;
+ for (const auto &operator_name : result) {
+ pp_result << "(" << operator_name.name << ", " << operator_name.language
+ << ") ";
+ }
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result["
+ << pp_result.str() << "]";
+ }
+ return result;
+}
+
+const ScopedVector<MobileOperatorInfo::MobileAPN> &
+MobileOperatorInfo::apn_list() const {
+ const auto &result = impl_->apn_list();
+ if (SLOG_IS_ON(Cellular, 3)) {
+ stringstream pp_result;
+ for (const auto &mobile_apn : result) {
+ pp_result << "(apn: " << mobile_apn->apn
+ << ", username: " << mobile_apn->username
+ << ", password: " << mobile_apn->password;
+ pp_result << ", operator_name_list: '";
+ for (const auto &operator_name : mobile_apn->operator_name_list) {
+ pp_result << "(" << operator_name.name << ", " << operator_name.language
+ << ") ";
+ }
+ pp_result << "') ";
+ }
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result["
+ << pp_result.str() << "]";
+ }
+ return result;
+}
+
+const vector<MobileOperatorInfo::OnlinePortal> &MobileOperatorInfo::olp_list()
+ const {
+ const auto &result = impl_->olp_list();
+ if (SLOG_IS_ON(Cellular, 3)) {
+ stringstream pp_result;
+ for (const auto &olp : result) {
+ pp_result << "(url: " << olp.url << ", method: " << olp.method
+ << ", post_data: " << olp.post_data << ") ";
+ }
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result["
+ << pp_result.str() << "]";
+ }
+ return result;
+}
+
+const string &MobileOperatorInfo::activation_code() const {
+ const auto &result = impl_->activation_code();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+bool MobileOperatorInfo::requires_roaming() const {
+ auto result = impl_->requires_roaming();
+ SLOG(this, 3) << GetLogPrefix(__func__) << ": Result[" << result << "]";
+ return result;
+}
+
+void MobileOperatorInfo::UpdateIMSI(const string &imsi) {
+ SLOG(this, 3) << GetLogPrefix(__func__) << "(" << imsi << ")";
+ impl_->UpdateIMSI(imsi);
+}
+
+void MobileOperatorInfo::UpdateICCID(const string &iccid) {
+ SLOG(this, 3) << GetLogPrefix(__func__) << "(" << iccid << ")";
+ impl_->UpdateICCID(iccid);
+}
+
+void MobileOperatorInfo::UpdateMCCMNC(const string &mccmnc) {
+ SLOG(this, 3) << GetLogPrefix(__func__) << "(" << mccmnc << ")";
+ impl_->UpdateMCCMNC(mccmnc);
+}
+
+void MobileOperatorInfo::UpdateSID(const string &sid) {
+ SLOG(this, 3) << GetLogPrefix(__func__) << "(" << sid << ")";
+ impl_->UpdateSID(sid);
+}
+
+void MobileOperatorInfo::UpdateNID(const string &nid) {
+ SLOG(this, 3) << GetLogPrefix(__func__) << "(" << nid << ")";
+ impl_->UpdateNID(nid);
+}
+
+void MobileOperatorInfo::UpdateOperatorName(const string &operator_name) {
+ SLOG(this, 3) << GetLogPrefix(__func__) << "(" << operator_name << ")";
+ impl_->UpdateOperatorName(operator_name);
+}
+
+void MobileOperatorInfo::UpdateOnlinePortal(const string &url,
+ const string &method,
+ const string &post_data) {
+ SLOG(this, 3) << GetLogPrefix(__func__)
+ << "(" << url
+ << ", " << method
+ << ", " << post_data << ")";
+ impl_->UpdateOnlinePortal(url, method, post_data);
+}
+
+void MobileOperatorInfo::Reset() {
+ SLOG(this, 3) << GetLogPrefix(__func__);
+ impl_->Reset();
+}
+
+} // namespace shill
diff --git a/cellular/mobile_operator_info.h b/cellular/mobile_operator_info.h
new file mode 100644
index 0000000..27bd6bc
--- /dev/null
+++ b/cellular/mobile_operator_info.h
@@ -0,0 +1,218 @@
+// Copyright (c) 2014 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.
+
+#ifndef SHILL_CELLULAR_MOBILE_OPERATOR_INFO_H_
+#define SHILL_CELLULAR_MOBILE_OPERATOR_INFO_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/memory/scoped_vector.h>
+
+namespace shill {
+
+class EventDispatcher;
+class MobileOperatorInfoImpl;
+
+// An MobileOperatorInfo object encapsulates the knowledge pertaining to all
+// mobile operators. Typical usage consists of three steps:
+// - Initialize the object, set database file paths for the operator
+// information.
+// - Add observers to be notified whenever an M[V]NO has been determined / any
+// information about the M[V]NO changes.
+// - Send operator information updates to the object.
+//
+// So a class Foo that wants to use this object typically looks like:
+//
+// class Foo {
+// class OperatorObserver : public MobileOperatorInfoObserver {
+// // Implement all Observer functions.
+// }
+// ...
+//
+// MobileOperatorInfo operator_info;
+// // Optional: Set a non-default database file.
+// operator_info.ClearDatabasePaths();
+// operator_info.AddDatabasePath(some_path);
+//
+// operator_info.Init(); // Required.
+//
+// OperatorObserver my_observer;
+// operator_info.AddObserver(my_observer);
+// ...
+// operator_info.UpdateIMSI(some_imsi);
+// operator_info.UpdateName(some_name);
+// ...
+// // Whenever enough information is available, |operator_info| notifies us
+// through |my_observer|.
+// };
+//
+class MobileOperatorInfo {
+ public:
+ class Observer {
+ public:
+ virtual ~Observer() {}
+
+ // This event fires when
+ // - A mobile [virtual] network operator
+ // - is first determined.
+ // - changes.
+ // - becomes invalid.
+ // - Some information about the known operator changes.
+ virtual void OnOperatorChanged() = 0;
+ };
+
+ // |Init| must be called on the constructed object before it is used.
+ // This object does not take ownership of dispatcher, and |dispatcher| is
+ // expected to outlive this object.
+ MobileOperatorInfo(EventDispatcher *dispatcher,
+ const std::string &info_owner);
+ virtual ~MobileOperatorInfo();
+
+ // These functions can be called before Init to read non default database
+ // file(s).
+ void ClearDatabasePaths();
+ void AddDatabasePath(const base::FilePath &absolute_path);
+
+ std::string GetLogPrefix(const char *func) const;
+ bool Init();
+
+ // Add/remove observers to subscribe to notifications.
+ void AddObserver(MobileOperatorInfo::Observer *observer);
+ void RemoveObserver(MobileOperatorInfo::Observer *observer);
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // Objects that encapsulate related information about the mobile operator.
+
+ // Encapsulates a name and the language that name has been localized to.
+ // The name can be a carrier name, or the name that a cellular carrier
+ // prefers to show for a certain access point.
+ struct LocalizedName {
+ // The name as it appears in the corresponding language.
+ std::string name;
+ // The language of this localized name. The format of a language is a two
+ // letter language code, e.g. 'en' for English.
+ // It is legal for an instance of LocalizedName to have an empty |language|
+ // field, as sometimes the underlying database does not contain that
+ // information.
+ std::string language;
+ };
+
+ // Encapsulates information on a mobile access point name. This information
+ // is usually necessary for 3GPP networks to be able to connect to a mobile
+ // network. So far, CDMA networks don't use this information.
+ struct MobileAPN {
+ // The access point url, which is fed to the modemmanager while connecting.
+ std::string apn;
+ // A list of localized names for this access point. Usually there is only
+ // one for each country that the associated cellular carrier operates in.
+ std::vector<LocalizedName> operator_name_list;
+ // The username and password fields that are required by the modemmanager.
+ // Either of these values can be empty if none is present. If a MobileAPN
+ // instance that is obtained from this parser contains a non-empty value
+ // for username/password, this usually means that the carrier requires
+ // a certain default pair.
+ std::string username;
+ std::string password;
+ };
+
+ // Encapsulates information about the Online payment portal used by chrome to
+ // redirect users for some carriers.
+ struct OnlinePortal {
+ std::string url;
+ std::string method;
+ std::string post_data;
+ };
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // Functions to obtain information about the current mobile operator.
+ // Any of these accessors can return an emtpy response if the information is
+ // not available. Use |IsMobileNetworkOperatorKnown| and
+ // |IsMobileVirtualNetworkOperatorKnown| to determine if a fix on the operator
+ // has been made. Note that the information returned by the other accessors is
+ // only valid when at least |IsMobileNetworkOperatorKnown| returns true. Their
+ // values are undefined otherwise.
+
+ // Query whether a mobile network operator has been successfully determined.
+ virtual bool IsMobileNetworkOperatorKnown() const;
+ // Query whether a mobile network operator has been successfully
+ // determined.
+ bool IsMobileVirtualNetworkOperatorKnown() const;
+
+
+ // The unique identifier of this carrier. This is primarily used to
+ // identify the user profile in store for each carrier. This identifier is
+ // access technology agnostic and should be the same across 3GPP and CDMA.
+ virtual const std::string &uuid() const;
+
+ virtual const std::string &operator_name() const;
+ virtual const std::string &country() const;
+ virtual const std::string &mccmnc() const;
+ const std::string &sid() const;
+ const std::string &nid() const;
+
+ // A given MVNO can be associated with multiple mcc/mnc pairs. A list of all
+ // associated mcc/mnc pairs concatenated together.
+ const std::vector<std::string> &mccmnc_list() const;
+ // A given MVNO can be associated with multiple sid(s). A list of all
+ // associated sid(s).
+ // There are likely many SID values associated with a CDMA carrier as they
+ // vary across regions and are more fine grained than countries. An important
+ // thing to keep in mind is that, since an SID contains fine grained
+ // information on where a modem is physically located, it should be regarded
+ // as user-sensitive information.
+ const std::vector<std::string> &sid_list() const;
+ // All localized names associated with this carrier entry.
+ const std::vector<LocalizedName> &operator_name_list() const;
+ // All access point names associated with this carrier entry.
+ const ScopedVector<MobileAPN> &apn_list() const;
+ // All Online Payment Portal URLs associated with this carrier entry. There
+ // are usually multiple OLPs based on access technology and it is up to the
+ // application to use the appropriate one.
+ virtual const std::vector<OnlinePortal> &olp_list() const;
+
+ // The number to dial for automatic activation.
+ virtual const std::string &activation_code() const;
+ // Some carriers are only available while roaming. This is mainly used by
+ // Chrome.
+ bool requires_roaming() const;
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // Functions used to notify this object of operator data changes.
+ // The Update* methods update the corresponding property of the network
+ // operator, and this value may be used to determine the M[V]NO.
+ // These values are also the values reported through accessors, overriding any
+ // information from the database.
+
+ // Throw away all information provided to the object, and start from top.
+ void Reset();
+
+ // Both MCCMNC and SID correspond to operator code in the different
+ // technologies. They are never to be used together. If you want to use SID
+ // after MCCMNC (or vice-versa), ensure a call to |Reset| to clear state.
+ virtual void UpdateMCCMNC(const std::string &mccmnc);
+ virtual void UpdateSID(const std::string &sid);
+
+ virtual void UpdateIMSI(const std::string &imsi);
+ void UpdateICCID(const std::string &iccid);
+ virtual void UpdateNID(const std::string &nid);
+ virtual void UpdateOperatorName(const std::string &operator_name);
+ void UpdateOnlinePortal(const std::string &url,
+ const std::string &method,
+ const std::string &post_data);
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // Expose implementation for test purposes only.
+ MobileOperatorInfoImpl * impl() { return impl_.get(); }
+
+ private:
+ std::unique_ptr<MobileOperatorInfoImpl> impl_;
+ DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfo);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOBILE_OPERATOR_INFO_H_
diff --git a/cellular/mobile_operator_info_impl.cc b/cellular/mobile_operator_info_impl.cc
new file mode 100644
index 0000000..43d4f86
--- /dev/null
+++ b/cellular/mobile_operator_info_impl.cc
@@ -0,0 +1,958 @@
+// Copyright (c) 2014 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/mobile_operator_info_impl.h"
+
+#include <regex.h>
+
+#include <algorithm>
+#include <cctype>
+#include <map>
+
+#include <base/bind.h>
+#include <base/strings/string_util.h>
+#include <google/protobuf/repeated_field.h>
+
+#include "shill/logging.h"
+#include "shill/protobuf_lite_streams.h"
+
+using base::Bind;
+using base::FilePath;
+using google::protobuf::io::CopyingInputStreamAdaptor;
+using google::protobuf::RepeatedField;
+using google::protobuf::RepeatedPtrField;
+using shill::mobile_operator_db::Data;
+using shill::mobile_operator_db::Filter;
+using shill::mobile_operator_db::LocalizedName;
+using shill::mobile_operator_db::MobileAPN;
+using shill::mobile_operator_db::MobileNetworkOperator;
+using shill::mobile_operator_db::MobileOperatorDB;
+using shill::mobile_operator_db::MobileVirtualNetworkOperator;
+using shill::mobile_operator_db::OnlinePortal;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(const MobileOperatorInfoImpl *m) {
+ return "(mobile_operator_info_impl)";
+}
+}
+
+// static
+const char *MobileOperatorInfoImpl::kDefaultDatabasePath =
+ "/usr/share/shill/serviceproviders.pbf";
+const int MobileOperatorInfoImpl::kMCCMNCMinLen = 5;
+
+namespace {
+
+// Wrap some low level functions from the GNU regex librarly.
+string GetRegError(int code, const regex_t *compiled) {
+ size_t length = regerror(code, compiled, nullptr, 0);
+ vector<char> buffer(length);
+ DCHECK_EQ(length, regerror(code, compiled, buffer.data(), length));
+ return buffer.data();
+}
+
+} // namespace
+
+MobileOperatorInfoImpl::MobileOperatorInfoImpl(EventDispatcher *dispatcher,
+ const string &info_owner)
+ : dispatcher_(dispatcher),
+ info_owner_(info_owner),
+ observers_(ObserverList<MobileOperatorInfo::Observer, true>::NOTIFY_ALL),
+ operator_code_type_(kOperatorCodeTypeUnknown),
+ current_mno_(nullptr),
+ current_mvno_(nullptr),
+ requires_roaming_(false),
+ user_olp_empty_(true),
+ weak_ptr_factory_(this) {
+ AddDatabasePath(FilePath(kDefaultDatabasePath));
+}
+
+MobileOperatorInfoImpl::~MobileOperatorInfoImpl() {}
+
+void MobileOperatorInfoImpl::ClearDatabasePaths() {
+ database_paths_.clear();
+}
+
+void MobileOperatorInfoImpl::AddDatabasePath(const FilePath &absolute_path) {
+ database_paths_.push_back(absolute_path);
+}
+
+bool MobileOperatorInfoImpl::Init() {
+ ScopedVector<MobileOperatorDB> databases;
+
+ // |database_| is guaranteed to be set once |Init| is called.
+ database_.reset(new MobileOperatorDB());
+
+ for (const auto &database_path : database_paths_) {
+ const char *database_path_cstr = database_path.value().c_str();
+ std::unique_ptr<CopyingInputStreamAdaptor> database_stream;
+ database_stream.reset(protobuf_lite_file_input_stream(database_path_cstr));
+ if (!database_stream.get()) {
+ LOG(ERROR) << "Failed to read mobile operator database: "
+ << database_path_cstr;
+ continue;
+ }
+
+ std::unique_ptr<MobileOperatorDB> database(new MobileOperatorDB());
+ if (!database->ParseFromZeroCopyStream(database_stream.get())) {
+ LOG(ERROR) << "Could not parse mobile operator database: "
+ << database_path_cstr;
+ continue;
+ }
+ LOG(INFO) << "Successfully loaded database: " << database_path_cstr;
+ // Hand over ownership to the vector.
+ databases.push_back(database.release());
+ }
+
+ // Collate all loaded databases into one.
+ if (databases.size() == 0) {
+ LOG(ERROR) << "Could not read any mobile operator database. "
+ << "Will not be able to determine MVNO.";
+ return false;
+ }
+
+ for (const auto &database : databases) {
+ // TODO(pprabhu) This merge might be very costly. Determine if we need to
+ // implement move semantics / bias the merge to use the largest database
+ // as the base database and merge other databases into it.
+ database_->MergeFrom(*database);
+ }
+ PreprocessDatabase();
+ return true;
+}
+
+void MobileOperatorInfoImpl::AddObserver(
+ MobileOperatorInfo::Observer *observer) {
+ observers_.AddObserver(observer);
+}
+
+void MobileOperatorInfoImpl::RemoveObserver(
+ MobileOperatorInfo::Observer *observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool MobileOperatorInfoImpl::IsMobileNetworkOperatorKnown() const {
+ return (current_mno_ != nullptr);
+}
+
+bool MobileOperatorInfoImpl::IsMobileVirtualNetworkOperatorKnown() const {
+ return (current_mvno_ != nullptr);
+}
+
+// ///////////////////////////////////////////////////////////////////////////
+// Getters.
+const string &MobileOperatorInfoImpl::info_owner() const {
+ return info_owner_;
+}
+
+const string &MobileOperatorInfoImpl::uuid() const {
+ return uuid_;
+}
+
+const string &MobileOperatorInfoImpl::operator_name() const {
+ // TODO(pprabhu) I'm not very sure yet what is the right thing to do here.
+ // It is possible that we obtain a name OTA, and then using some other
+ // information (say the iccid range), determine that this is an MVNO. In
+ // that case, we may want to *override* |user_operator_name_| by the name
+ // obtained from the DB for the MVNO.
+ return operator_name_;
+}
+
+const string &MobileOperatorInfoImpl::country() const {
+ return country_;
+}
+
+const string &MobileOperatorInfoImpl::mccmnc() const {
+ return mccmnc_;
+}
+
+const string &MobileOperatorInfoImpl::MobileOperatorInfoImpl::sid() const {
+ return sid_;
+}
+
+const string &MobileOperatorInfoImpl::nid() const {
+ return (user_nid_ == "") ? nid_ : user_nid_;
+}
+
+const vector<string> &MobileOperatorInfoImpl::mccmnc_list() const {
+ return mccmnc_list_;
+}
+
+const vector<string> &MobileOperatorInfoImpl::sid_list() const {
+ return sid_list_;
+}
+
+const vector<MobileOperatorInfo::LocalizedName> &
+MobileOperatorInfoImpl::operator_name_list() const {
+ return operator_name_list_;
+}
+
+const ScopedVector<MobileOperatorInfo::MobileAPN> &
+MobileOperatorInfoImpl::apn_list() const {
+ return apn_list_;
+}
+
+const vector<MobileOperatorInfo::OnlinePortal> &
+MobileOperatorInfoImpl::olp_list() const {
+ return olp_list_;
+}
+
+const string &MobileOperatorInfoImpl::activation_code() const {
+ return activation_code_;
+}
+
+bool MobileOperatorInfoImpl::requires_roaming() const {
+ return requires_roaming_;
+}
+
+// ///////////////////////////////////////////////////////////////////////////
+// Functions used to notify this object of operator data changes.
+void MobileOperatorInfoImpl::UpdateIMSI(const string &imsi) {
+ bool operator_changed = false;
+ if (user_imsi_ == imsi) {
+ return;
+ }
+
+ user_imsi_ = imsi;
+
+ if (!user_mccmnc_.empty()) {
+ if (!StartsWithASCII(imsi, user_mccmnc_, false)) {
+ LOG(WARNING) << "MCCMNC [" << user_mccmnc_ << "] is not a substring of "
+ << "the IMSI [" << imsi << "].";
+ }
+ } else {
+ // Attempt to determine the MNO from IMSI since MCCMNC is absent.
+ AppendToCandidatesByMCCMNC(imsi.substr(0, kMCCMNCMinLen));
+ AppendToCandidatesByMCCMNC(imsi.substr(0, kMCCMNCMinLen + 1));
+ if (!candidates_by_operator_code_.empty()) {
+ // We found some candidates using IMSI.
+ operator_changed |= UpdateMNO();
+ }
+ }
+ operator_changed |= UpdateMVNO();
+
+ // No special notification should be sent for this property, since the object
+ // does not expose |imsi| as a property at all.
+ if (operator_changed) {
+ PostNotifyOperatorChanged();
+ }
+}
+
+void MobileOperatorInfoImpl::UpdateICCID(const string &iccid) {
+ if (user_iccid_ == iccid) {
+ return;
+ }
+
+ user_iccid_ = iccid;
+ // |iccid| is not an exposed property, so don't raise event for just this
+ // property update.
+ if (UpdateMVNO()) {
+ PostNotifyOperatorChanged();
+ }
+}
+
+void MobileOperatorInfoImpl::UpdateMCCMNC(const string &mccmnc) {
+ if (user_mccmnc_ == mccmnc) {
+ return;
+ }
+
+ user_mccmnc_ = mccmnc;
+ HandleMCCMNCUpdate();
+ candidates_by_operator_code_.clear();
+ AppendToCandidatesByMCCMNC(mccmnc);
+
+ // Always update M[V]NO, even if we found no candidates, since we might have
+ // lost some candidates due to an incorrect MCCMNC.
+ bool operator_changed = false;
+ operator_changed |= UpdateMNO();
+ operator_changed |= UpdateMVNO();
+ if (operator_changed || ShouldNotifyPropertyUpdate()) {
+ PostNotifyOperatorChanged();
+ }
+}
+
+void MobileOperatorInfoImpl::UpdateSID(const string &sid) {
+ if (user_sid_ == sid) {
+ return;
+ }
+
+ user_sid_ = sid;
+ HandleSIDUpdate();
+ candidates_by_operator_code_.clear();
+ AppendToCandidatesBySID(sid);
+
+ // Always update M[V]NO, even if we found no candidates, since we might have
+ // lost some candidates due to an incorrect SID.
+ bool operator_changed = false;
+ operator_changed |= UpdateMNO();
+ operator_changed |= UpdateMVNO();
+ if (operator_changed || ShouldNotifyPropertyUpdate()) {
+ PostNotifyOperatorChanged();
+ }
+}
+
+void MobileOperatorInfoImpl::UpdateNID(const string &nid) {
+ if (user_nid_ == nid) {
+ return;
+ }
+
+ user_nid_ = nid;
+ if (UpdateMVNO() || ShouldNotifyPropertyUpdate()) {
+ PostNotifyOperatorChanged();
+ }
+}
+
+void MobileOperatorInfoImpl::UpdateOperatorName(const string &operator_name) {
+ bool operator_changed = false;
+ if (user_operator_name_ == operator_name) {
+ return;
+ }
+
+ user_operator_name_ = operator_name;
+ HandleOperatorNameUpdate();
+
+ // We must update the candidates by name anyway.
+ StringToMNOListMap::const_iterator cit = name_to_mnos_.find(
+ NormalizeOperatorName(operator_name));
+ candidates_by_name_.clear();
+ if (cit != name_to_mnos_.end()) {
+ candidates_by_name_ = cit->second;
+ // We should never have inserted an empty vector into the map.
+ DCHECK(!candidates_by_name_.empty());
+ } else {
+ LOG(INFO) << "Operator name [" << operator_name << "] "
+ << "(Normalized: [" << NormalizeOperatorName(operator_name)
+ << "]) does not match any MNO.";
+ }
+
+ operator_changed |= UpdateMNO();
+ operator_changed |= UpdateMVNO();
+ if (operator_changed || ShouldNotifyPropertyUpdate()) {
+ PostNotifyOperatorChanged();
+ }
+}
+
+void MobileOperatorInfoImpl::UpdateOnlinePortal(const string &url,
+ const string &method,
+ const string &post_data) {
+ if (!user_olp_empty_ &&
+ user_olp_.url == url &&
+ user_olp_.method == method &&
+ user_olp_.post_data == post_data) {
+ return;
+ }
+
+ user_olp_empty_ = false;
+ user_olp_.url = url;
+ user_olp_.method = method;
+ user_olp_.post_data = post_data;
+ HandleOnlinePortalUpdate();
+
+ // OnlinePortal is never used in deciding M[V]NO.
+ if (ShouldNotifyPropertyUpdate()) {
+ PostNotifyOperatorChanged();
+ }
+}
+
+void MobileOperatorInfoImpl::Reset() {
+ bool should_notify = current_mno_ != nullptr || current_mvno_ != nullptr;
+
+ current_mno_ = nullptr;
+ current_mvno_ = nullptr;
+ operator_code_type_ = kOperatorCodeTypeUnknown;
+ candidates_by_operator_code_.clear();
+ candidates_by_name_.clear();
+
+ ClearDBInformation();
+
+ user_imsi_.clear();
+ user_iccid_.clear();
+ user_mccmnc_.clear();
+ user_sid_.clear();
+ user_nid_.clear();
+ user_operator_name_.clear();
+ user_olp_empty_ = true;
+ user_olp_.url.clear();
+ user_olp_.method.clear();
+ user_olp_.post_data.clear();
+
+ if (should_notify) {
+ PostNotifyOperatorChanged();
+ }
+}
+
+void MobileOperatorInfoImpl::PreprocessDatabase() {
+ SLOG(this, 3) << __func__;
+
+ mccmnc_to_mnos_.clear();
+ sid_to_mnos_.clear();
+ name_to_mnos_.clear();
+
+ const RepeatedPtrField<MobileNetworkOperator> &mnos = database_->mno();
+ for (const auto &mno : mnos) {
+ // MobileNetworkOperator::data is a required field.
+ DCHECK(mno.has_data());
+ const Data &data = mno.data();
+
+ const RepeatedPtrField<string> &mccmncs = data.mccmnc();
+ for (const auto &mccmnc : mccmncs) {
+ InsertIntoStringToMNOListMap(&mccmnc_to_mnos_, mccmnc, &mno);
+ }
+
+ const RepeatedPtrField<string> &sids = data.sid();
+ for (const auto &sid : sids) {
+ InsertIntoStringToMNOListMap(&sid_to_mnos_, sid, &mno);
+ }
+
+ const RepeatedPtrField<LocalizedName> &localized_names =
+ data.localized_name();
+ for (const auto &localized_name : localized_names) {
+ // LocalizedName::name is a required field.
+ DCHECK(localized_name.has_name());
+ InsertIntoStringToMNOListMap(&name_to_mnos_,
+ NormalizeOperatorName(localized_name.name()),
+ &mno);
+ }
+ }
+
+ if (database_->imvno_size() > 0) {
+ // TODO(pprabhu) Support IMVNOs.
+ LOG(ERROR) << "InternationalMobileVirtualNetworkOperators are not "
+ << "supported yet. Ignoring all IMVNOs.";
+ }
+}
+
+// This function assumes that duplicate |values| are never inserted for the
+// same |key|. If you do that, the function is too dumb to deduplicate the
+// |value|s, and two copies will get stored.
+void MobileOperatorInfoImpl::InsertIntoStringToMNOListMap(
+ StringToMNOListMap *table,
+ const string &key,
+ const MobileNetworkOperator *value) {
+ (*table)[key].push_back(value);
+}
+
+bool MobileOperatorInfoImpl::AppendToCandidatesByMCCMNC(const string &mccmnc) {
+ // First check that we haven't determined candidates using SID.
+ if (operator_code_type_ == kOperatorCodeTypeSID) {
+ LOG(WARNING) << "SID update will be overridden by the MCCMNC update for "
+ "determining MNO.";
+ candidates_by_operator_code_.clear();
+ }
+
+ operator_code_type_ = kOperatorCodeTypeMCCMNC;
+ StringToMNOListMap::const_iterator cit = mccmnc_to_mnos_.find(mccmnc);
+ if (cit == mccmnc_to_mnos_.end()) {
+ LOG(WARNING) << "Unknown MCCMNC value [" << mccmnc << "].";
+ return false;
+ }
+
+ // We should never have inserted an empty vector into the map.
+ DCHECK(!cit->second.empty());
+ for (const auto &mno : cit->second) {
+ candidates_by_operator_code_.push_back(mno);
+ }
+ return true;
+}
+
+bool MobileOperatorInfoImpl::AppendToCandidatesBySID(const string &sid) {
+ // First check that we haven't determined candidates using MCCMNC.
+ if (operator_code_type_ == kOperatorCodeTypeMCCMNC) {
+ LOG(WARNING) << "MCCMNC update will be overriden by the SID update for "
+ "determining MNO.";
+ candidates_by_operator_code_.clear();
+ }
+
+ operator_code_type_ = kOperatorCodeTypeSID;
+ StringToMNOListMap::const_iterator cit = sid_to_mnos_.find(sid);
+ if (cit == sid_to_mnos_.end()) {
+ LOG(WARNING) << "Unknown SID value [" << sid << "].";
+ return false;
+ }
+
+ // We should never have inserted an empty vector into the map.
+ DCHECK(!cit->second.empty());
+ for (const auto &mno : cit->second) {
+ candidates_by_operator_code_.push_back(mno);
+ }
+ return true;
+}
+
+string MobileOperatorInfoImpl::OperatorCodeString() const {
+ switch (operator_code_type_) {
+ case kOperatorCodeTypeMCCMNC:
+ return "MCCMNC";
+ case kOperatorCodeTypeSID:
+ return "SID";
+ case kOperatorCodeTypeUnknown: // FALLTHROUGH
+ default:
+ return "UnknownOperatorCodeType";
+ }
+}
+
+bool MobileOperatorInfoImpl::UpdateMNO() {
+ SLOG(this, 3) << __func__;
+ const MobileNetworkOperator *candidate = nullptr;
+
+ // The only way |operator_code_type_| can be |kOperatorCodeTypeUnknown| is
+ // that we haven't received any operator_code updates yet.
+ DCHECK(operator_code_type_ == kOperatorCodeTypeMCCMNC ||
+ operator_code_type_ == kOperatorCodeTypeSID ||
+ (user_mccmnc_.empty() && user_sid_.empty()));
+
+ // TODO(pprabhu) Remove this despicable hack. (crosbug.com/p/30200)
+ // We currently have no principled way to handle an MVNO for which the
+ // database does not have MCCMNC data. It is possible that some other MNO
+ // matches the MCCMNC, while the MVNO matches the operator name. We special
+ // case one such operator here and override all the logic below.
+ const char kCubicUUID[] = "2de39b14-c3ba-4143-abb5-c67a390034ee";
+ for (auto candidate_by_name : candidates_by_name_) {
+ CHECK(candidate_by_name->has_data());
+ CHECK(candidate_by_name->data().has_uuid());
+ if (candidate_by_name->data().uuid() == kCubicUUID) {
+ current_mno_ = candidate_by_name;
+ RefreshDBInformation();
+ return true;
+ }
+ }
+
+ if (candidates_by_operator_code_.size() == 1) {
+ candidate = candidates_by_operator_code_[0];
+ if (candidates_by_name_.size() > 0) {
+ bool found_match = false;
+ for (auto candidate_by_name : candidates_by_name_) {
+ if (candidate_by_name == candidate) {
+ found_match = true;
+ break;
+ }
+ }
+ if (!found_match) {
+ const string &operator_code =
+ (operator_code_type_ == kOperatorCodeTypeMCCMNC) ? user_mccmnc_ :
+ user_sid_;
+ SLOG(this, 1) << "MNO determined by "
+ << OperatorCodeString() << " [" << operator_code
+ << "] does not match any suggested by name["
+ << user_operator_name_
+ << "]. "
+ << OperatorCodeString() << " overrides name!";
+ }
+ }
+ } else if (candidates_by_operator_code_.size() > 1) {
+ // Try to find an intersection of the two candidate lists. These lists
+ // should be almost always of length 1. Simply iterate.
+ for (auto candidate_by_mccmnc : candidates_by_operator_code_) {
+ for (auto candidate_by_name : candidates_by_name_) {
+ if (candidate_by_mccmnc == candidate_by_name) {
+ candidate = candidate_by_mccmnc;
+ break;
+ }
+ }
+ if (candidate != nullptr) {
+ break;
+ }
+ }
+ if (candidate == nullptr) {
+ const string &operator_code =
+ (operator_code_type_ == kOperatorCodeTypeMCCMNC) ? user_mccmnc_ :
+ user_sid_;
+ SLOG(this, 1) << "MNOs suggested by "
+ << OperatorCodeString() << " [" << operator_code
+ << "] are multiple and disjoint from those suggested "
+ << "by name["
+ << user_operator_name_
+ << "].";
+ candidate = PickOneFromDuplicates(candidates_by_operator_code_);
+ }
+ } else { // candidates_by_operator_code_.size() == 0
+ // Special case: In case we had a *wrong* operator_code update, we want
+ // to override the suggestions from |user_operator_name_|. We should not
+ // determine an MNO in this case.
+ if ((operator_code_type_ == kOperatorCodeTypeMCCMNC &&
+ !user_mccmnc_.empty()) ||
+ (operator_code_type_ == kOperatorCodeTypeSID && !user_sid_.empty())) {
+ SLOG(this, 1) << "A non-matching "
+ << OperatorCodeString() << " "
+ << "was reported by the user."
+ << "We fail the MNO match in this case.";
+ } else if (candidates_by_name_.size() == 1) {
+ candidate = candidates_by_name_[0];
+ } else if (candidates_by_name_.size() > 1) {
+ SLOG(this, 1) << "Multiple MNOs suggested by name["
+ << user_operator_name_
+ << "], and none by MCCMNC.";
+ candidate = PickOneFromDuplicates(candidates_by_name_);
+ } else { // candidates_by_name_.size() == 0
+ SLOG(this, 1) << "No candidates suggested.";
+ }
+ }
+
+ if (candidate != current_mno_) {
+ current_mno_ = candidate;
+ RefreshDBInformation();
+ return true;
+ }
+ return false;
+}
+
+bool MobileOperatorInfoImpl::UpdateMVNO() {
+ SLOG(this, 3) << __func__;
+ if (current_mno_ == nullptr) {
+ return false;
+ }
+
+ for (const auto &candidate_mvno : current_mno_->mvno()) {
+ bool passed_all_filters = true;
+ for (const auto &filter : candidate_mvno.mvno_filter()) {
+ if (!FilterMatches(filter)) {
+ passed_all_filters = false;
+ break;
+ }
+ }
+ if (passed_all_filters) {
+ if (current_mvno_ == &candidate_mvno) {
+ return false;
+ }
+ current_mvno_ = &candidate_mvno;
+ RefreshDBInformation();
+ return true;
+ }
+ }
+
+ // We did not find any valid MVNO.
+ if (current_mvno_ != nullptr) {
+ current_mvno_ = nullptr;
+ RefreshDBInformation();
+ return true;
+ }
+ return false;
+}
+
+const MobileNetworkOperator *MobileOperatorInfoImpl::PickOneFromDuplicates(
+ const vector<const MobileNetworkOperator*> &duplicates) const {
+ if (duplicates.empty())
+ return nullptr;
+
+ for (auto candidate : duplicates) {
+ if (candidate->earmarked()) {
+ SLOG(this, 2) << "Picking earmarked candidate: "
+ << candidate->data().uuid();
+ return candidate;
+ }
+ }
+ SLOG(this, 2) << "No earmarked candidate found. Choosing the first.";
+ return duplicates[0];
+}
+
+bool MobileOperatorInfoImpl::FilterMatches(const Filter &filter) {
+ DCHECK(filter.has_regex());
+ string to_match;
+ switch (filter.type()) {
+ case mobile_operator_db::Filter_Type_IMSI:
+ to_match = user_imsi_;
+ break;
+ case mobile_operator_db::Filter_Type_ICCID:
+ to_match = user_iccid_;
+ break;
+ case mobile_operator_db::Filter_Type_SID:
+ to_match = user_sid_;
+ break;
+ case mobile_operator_db::Filter_Type_OPERATOR_NAME:
+ to_match = user_operator_name_;
+ break;
+ case mobile_operator_db::Filter_Type_MCCMNC:
+ to_match = user_mccmnc_;
+ break;
+ default:
+ SLOG(this, 1) << "Unknown filter type [" << filter.type() << "]";
+ return false;
+ }
+ // |to_match| can be empty if we have no *user provided* information of the
+ // correct type.
+ if (to_match.empty()) {
+ SLOG(this, 2) << "Nothing to match against (filter: "
+ << filter.regex() << ").";
+ return false;
+ }
+
+ // Must use GNU regex implementation, since C++11 implementation is
+ // incomplete.
+ regex_t filter_regex;
+ string filter_regex_str = filter.regex();
+
+ // |regexec| matches the given regular expression to a substring of the
+ // given query string. Ensure that |filter_regex_str| uses anchors to
+ // accept only a full match.
+ if (filter_regex_str.front() != '^') {
+ filter_regex_str = "^" + filter_regex_str;
+ }
+ if (filter_regex_str.back() != '$') {
+ filter_regex_str = filter_regex_str + "$";
+ }
+
+ int regcomp_error = regcomp(&filter_regex,
+ filter_regex_str.c_str(),
+ REG_EXTENDED | REG_NOSUB);
+ if (regcomp_error) {
+ LOG(WARNING) << "Could not compile regex '" << filter.regex() << "'. "
+ << "Error returned: "
+ << GetRegError(regcomp_error, &filter_regex) << ". ";
+ regfree(&filter_regex);
+ return false;
+ }
+
+ int regexec_error = regexec(&filter_regex,
+ to_match.c_str(),
+ 0,
+ nullptr,
+ 0);
+ if (regexec_error) {
+ string error_string;
+ error_string = GetRegError(regcomp_error, &filter_regex);
+ SLOG(this, 2) << "Could not match string " << to_match << " "
+ << "against regexp " << filter.regex() << ". "
+ << "Error returned: " << error_string << ". ";
+ regfree(&filter_regex);
+ return false;
+ }
+ regfree(&filter_regex);
+ return true;
+}
+
+void MobileOperatorInfoImpl::RefreshDBInformation() {
+ ClearDBInformation();
+
+ if (current_mno_ == nullptr) {
+ return;
+ }
+
+ // |data| is a required field.
+ DCHECK(current_mno_->has_data());
+ SLOG(this, 2) << "Reloading MNO data.";
+ ReloadData(current_mno_->data());
+
+ if (current_mvno_ != nullptr) {
+ // |data| is a required field.
+ DCHECK(current_mvno_->has_data());
+ SLOG(this, 2) << "Reloading MVNO data.";
+ ReloadData(current_mvno_->data());
+ }
+}
+
+void MobileOperatorInfoImpl::ClearDBInformation() {
+ uuid_.clear();
+ country_.clear();
+ nid_.clear();
+ mccmnc_list_.clear();
+ HandleMCCMNCUpdate();
+ sid_list_.clear();
+ HandleSIDUpdate();
+ operator_name_list_.clear();
+ HandleOperatorNameUpdate();
+ apn_list_.clear();
+ olp_list_.clear();
+ raw_olp_list_.clear();
+ HandleOnlinePortalUpdate();
+ activation_code_.clear();
+ requires_roaming_ = false;
+}
+
+void MobileOperatorInfoImpl::ReloadData(const Data &data) {
+ SLOG(this, 3) << __func__;
+ // |uuid_| is *always* overwritten. An MNO and MVNO should not share the
+ // |uuid_|.
+ CHECK(data.has_uuid());
+ uuid_ = data.uuid();
+
+ if (data.has_country()) {
+ country_ = data.country();
+ }
+
+ if (data.localized_name_size() > 0) {
+ operator_name_list_.clear();
+ for (const auto &localized_name : data.localized_name()) {
+ operator_name_list_.push_back({localized_name.name(),
+ localized_name.language()});
+ }
+ HandleOperatorNameUpdate();
+ }
+
+ if (data.has_requires_roaming()) {
+ requires_roaming_ = data.requires_roaming();
+ }
+
+ if (data.olp_size() > 0) {
+ raw_olp_list_.clear();
+ // Copy the olp list so we can mutate it.
+ for (const auto &olp : data.olp()) {
+ raw_olp_list_.push_back(olp);
+ }
+ HandleOnlinePortalUpdate();
+ }
+
+ if (data.mccmnc_size() > 0) {
+ mccmnc_list_.clear();
+ for (const auto &mccmnc : data.mccmnc()) {
+ mccmnc_list_.push_back(mccmnc);
+ }
+ HandleMCCMNCUpdate();
+ }
+
+ if (data.mobile_apn_size() > 0) {
+ apn_list_.clear();
+ for (const auto &apn_data : data.mobile_apn()) {
+ auto *apn = new MobileOperatorInfo::MobileAPN();
+ apn->apn = apn_data.apn();
+ apn->username = apn_data.username();
+ apn->password = apn_data.password();
+ for (const auto &localized_name : apn_data.localized_name()) {
+ apn->operator_name_list.push_back({localized_name.name(),
+ localized_name.language()});
+ }
+
+ // Takes ownership.
+ apn_list_.push_back(apn);
+ }
+ }
+
+ if (data.sid_size() > 0) {
+ sid_list_.clear();
+ for (const auto &sid : data.sid()) {
+ sid_list_.push_back(sid);
+ }
+ HandleSIDUpdate();
+ }
+
+ if (data.has_activation_code()) {
+ activation_code_ = data.activation_code();
+ }
+}
+
+void MobileOperatorInfoImpl::HandleMCCMNCUpdate() {
+ if (!user_mccmnc_.empty()) {
+ bool append_to_list = true;
+ for (const auto &mccmnc : mccmnc_list_) {
+ append_to_list &= (user_mccmnc_ != mccmnc);
+ }
+ if (append_to_list) {
+ mccmnc_list_.push_back(user_mccmnc_);
+ }
+ }
+
+ if (!user_mccmnc_.empty()) {
+ mccmnc_ = user_mccmnc_;
+ } else if (mccmnc_list_.size() > 0) {
+ mccmnc_ = mccmnc_list_[0];
+ } else {
+ mccmnc_.clear();
+ }
+}
+
+void MobileOperatorInfoImpl::HandleOperatorNameUpdate() {
+ if (!user_operator_name_.empty()) {
+ bool append_user_operator_name = true;
+ for (const auto &localized_name : operator_name_list_) {
+ append_user_operator_name &= (user_operator_name_ != localized_name.name);
+ }
+ if (append_user_operator_name) {
+ MobileOperatorInfo::LocalizedName localized_name {
+ user_operator_name_,
+ ""};
+ operator_name_list_.push_back(localized_name);
+ }
+ }
+
+ if (!operator_name_list_.empty()) {
+ operator_name_ = operator_name_list_[0].name;
+ } else if (!user_operator_name_.empty()) {
+ operator_name_ = user_operator_name_;
+ } else {
+ operator_name_.clear();
+ }
+}
+
+void MobileOperatorInfoImpl::HandleSIDUpdate() {
+ if (!user_sid_.empty()) {
+ bool append_user_sid = true;
+ for (const auto &sid : sid_list_) {
+ append_user_sid &= (user_sid_ != sid);
+ }
+ if (append_user_sid) {
+ sid_list_.push_back(user_sid_);
+ }
+ }
+
+ if (!user_sid_.empty()) {
+ sid_ = user_sid_;
+ } else if (sid_list_.size() > 0) {
+ sid_ = sid_list_[0];
+ } else {
+ sid_.clear();
+ }
+}
+
+// Warning: Currently, an MCCMNC/SID update by itself does not result into
+// recomputation of the |olp_list_|. This means that if the new MCCMNC/SID
+// causes an online portal filter to match, we'll miss that.
+// This won't be a problem if either the MNO or the MVNO changes, since data is
+// reloaded then.
+// This is a corner case that we don't expect to hit, since MCCMNC doesn't
+// really change in a running system.
+void MobileOperatorInfoImpl::HandleOnlinePortalUpdate() {
+ // Always recompute |olp_list_|. We don't expect this list to be big.
+ olp_list_.clear();
+ for (const auto &raw_olp : raw_olp_list_) {
+ if (!raw_olp.has_olp_filter() || FilterMatches(raw_olp.olp_filter())) {
+ olp_list_.push_back(MobileOperatorInfo::OnlinePortal {
+ raw_olp.url(),
+ (raw_olp.method() == raw_olp.GET) ? "GET" : "POST",
+ raw_olp.post_data()});
+ }
+ }
+ if (!user_olp_empty_) {
+ bool append_user_olp = true;
+ for (const auto &olp : olp_list_) {
+ append_user_olp &= (olp.url != user_olp_.url ||
+ olp.method != user_olp_.method ||
+ olp.post_data != user_olp_.post_data);
+ }
+ if (append_user_olp) {
+ olp_list_.push_back(user_olp_);
+ }
+ }
+}
+
+void MobileOperatorInfoImpl::PostNotifyOperatorChanged() {
+ SLOG(this, 3) << __func__;
+ // If there was an outstanding task, it will get replaced.
+ notify_operator_changed_task_.Reset(
+ Bind(&MobileOperatorInfoImpl::NotifyOperatorChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+ dispatcher_->PostTask(notify_operator_changed_task_.callback());
+}
+
+void MobileOperatorInfoImpl::NotifyOperatorChanged() {
+ FOR_EACH_OBSERVER(MobileOperatorInfo::Observer,
+ observers_,
+ OnOperatorChanged());
+}
+
+bool MobileOperatorInfoImpl::ShouldNotifyPropertyUpdate() const {
+ return IsMobileNetworkOperatorKnown() ||
+ IsMobileVirtualNetworkOperatorKnown();
+}
+
+string MobileOperatorInfoImpl::NormalizeOperatorName(const string &name) const {
+ string result = base::StringToLowerASCII(name);
+ base::RemoveChars(result, base::kWhitespaceASCII, &result);
+ return result;
+}
+
+} // namespace shill
diff --git a/cellular/mobile_operator_info_impl.h b/cellular/mobile_operator_info_impl.h
new file mode 100644
index 0000000..5e7f0d2
--- /dev/null
+++ b/cellular/mobile_operator_info_impl.h
@@ -0,0 +1,222 @@
+// Copyright (c) 2014 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.
+
+#ifndef SHILL_CELLULAR_MOBILE_OPERATOR_INFO_IMPL_H_
+#define SHILL_CELLULAR_MOBILE_OPERATOR_INFO_IMPL_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/cancelable_callback.h>
+#include <base/files/file_util.h>
+#include <base/memory/scoped_vector.h>
+#include <base/memory/weak_ptr.h>
+#include <base/observer_list.h>
+
+#include "shill/cellular/mobile_operator_info.h"
+#include "shill/event_dispatcher.h"
+#include "shill/proto_bindings/mobile_operator_db/mobile_operator_db.pb.h"
+
+namespace shill {
+
+class MobileOperatorInfoImpl {
+ public:
+ typedef
+ std::map<std::string,
+ std::vector<const mobile_operator_db::MobileNetworkOperator *>>
+ StringToMNOListMap;
+
+ MobileOperatorInfoImpl(EventDispatcher *dispatcher,
+ const std::string &info_owner);
+ ~MobileOperatorInfoImpl();
+
+ // API functions of the interface.
+ // See mobile_operator_info_impl.h for details.
+ void ClearDatabasePaths();
+ void AddDatabasePath(const base::FilePath &absolute_path);
+ bool Init();
+ void AddObserver(MobileOperatorInfo::Observer *observer);
+ void RemoveObserver(MobileOperatorInfo::Observer *observer);
+ bool IsMobileNetworkOperatorKnown() const;
+ bool IsMobileVirtualNetworkOperatorKnown() const;
+ const std::string &info_owner() const;
+ const std::string &uuid() const;
+ const std::string &operator_name() const;
+ const std::string &country() const;
+ const std::string &mccmnc() const;
+ const std::string &sid() const;
+ const std::string &nid() const;
+ const std::vector<std::string> &mccmnc_list() const;
+ const std::vector<std::string> &sid_list() const;
+ const std::vector<MobileOperatorInfo::LocalizedName>
+ &operator_name_list() const;
+ const ScopedVector<MobileOperatorInfo::MobileAPN> &apn_list() const;
+ const std::vector<MobileOperatorInfo::OnlinePortal> &olp_list() const;
+ const std::string &activation_code() const;
+ bool requires_roaming() const;
+ void Reset();
+ void UpdateIMSI(const std::string &imsi);
+ void UpdateICCID(const std::string &iccid);
+ void UpdateMCCMNC(const std::string &mccmnc);
+ void UpdateSID(const std::string &sid);
+ void UpdateNID(const std::string &nid);
+ void UpdateOperatorName(const std::string &operator_name);
+ void UpdateOnlinePortal(const std::string &url,
+ const std::string &method,
+ const std::string &post_data);
+
+ private:
+ friend class MobileOperatorInfoInitTest;
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // Static variables.
+ // Default databases to load.
+ static const char *kDefaultDatabasePath;
+ // MCCMNC can be of length 5 or 6. When using this constant, keep in mind that
+ // the length of MCCMNC can by |kMCCMNCMinLen| or |kMCCMNCMinLen + 1|.
+ static const int kMCCMNCMinLen;
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // Functions.
+ void PreprocessDatabase();
+ // This function assumes that duplicate |values| are never inserted for the
+ // same |key|. If you do that, the function is too dumb to deduplicate the
+ // |value|s, and two copies will get stored.
+ void InsertIntoStringToMNOListMap(
+ StringToMNOListMap *table,
+ const std::string &key,
+ const mobile_operator_db::MobileNetworkOperator *value);
+
+ bool UpdateMNO();
+ bool UpdateMVNO();
+ bool FilterMatches(const shill::mobile_operator_db::Filter &filter);
+ const mobile_operator_db::MobileNetworkOperator *PickOneFromDuplicates(
+ const std::vector<const mobile_operator_db::MobileNetworkOperator*>
+ &duplicates) const;
+ // Reloads the information about M[V]NO from the database.
+ void RefreshDBInformation();
+ void ClearDBInformation();
+ // Reload all data from |data|.
+ // Semantics: If a field data.x exists, then it *overwrites* the current
+ // information gained from data.x. E.g., if |data.name_size() > 0| is true,
+ // then we replace *all* names. Otherwise, we leave names untouched.
+ // This allows MVNOs to overwrite information obtained from the corresponding
+ // MNO.
+ void ReloadData(const mobile_operator_db::Data &data);
+ // Append candidates recognized by |mccmnc| to the candidate list.
+ bool AppendToCandidatesByMCCMNC(const std::string &mccmnc);
+ bool AppendToCandidatesBySID(const std::string &sid);
+ std::string OperatorCodeString() const;
+
+ // Notifies all observers that the operator has changed.
+ void PostNotifyOperatorChanged();
+ // The actual notification is sent out here. This should not be called
+ // directly from any function.
+ void NotifyOperatorChanged();
+
+ // For a property update that does not result in an M[V]NO update, this
+ // function determines whether observers should be notified anyway.
+ bool ShouldNotifyPropertyUpdate() const;
+
+ // OperatorName comparisons for determining the MNO are done after normalizing
+ // the names to ignore case and spaces.
+ std::string NormalizeOperatorName(const std::string &name) const;
+
+ // These functions encapsulate the logic to update different properties
+ // properly whenever an update is either received from the user or the
+ // database.
+ void HandleMCCMNCUpdate();
+ void HandleOperatorNameUpdate();
+ void HandleSIDUpdate();
+ void HandleOnlinePortalUpdate();
+
+ // Accessor functions for testing purpose only.
+ mobile_operator_db::MobileOperatorDB *database() {
+ return database_.get();
+ }
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // Data.
+ // Not owned by MobileOperatorInfoImpl.
+ EventDispatcher *const dispatcher_;
+
+ const std::string info_owner_;
+
+ // Owned by MobileOperatorInfoImpl, may be created externally.
+ std::vector<base::FilePath> database_paths_;
+
+ // Owned and modified only by MobileOperatorInfoImpl.
+ // The observers added to this list are not owned by this object. Moreover,
+ // the observer is likely to outlive this object. We do enforce removal of all
+ // observers before this object is destroyed.
+ ObserverList<MobileOperatorInfo::Observer> observers_;
+ base::CancelableClosure notify_operator_changed_task_;
+
+ std::unique_ptr<mobile_operator_db::MobileOperatorDB> database_;
+ StringToMNOListMap mccmnc_to_mnos_;
+ StringToMNOListMap sid_to_mnos_;
+ StringToMNOListMap name_to_mnos_;
+
+ // |candidates_by_operator_code| can be determined either using MCCMNC or
+ // using SID. At any one time, we only expect one of these operator codes to
+ // be updated by the user. We use |operator_code_type_| to keep track of which
+ // update we have received and warn the user if we receive both.
+ enum OperatorCodeType {
+ kOperatorCodeTypeUnknown = 0,
+ kOperatorCodeTypeMCCMNC,
+ kOperatorCodeTypeSID,
+ };
+ OperatorCodeType operator_code_type_;
+ std::vector<const mobile_operator_db::MobileNetworkOperator *>
+ candidates_by_operator_code_;
+
+ std::vector<const mobile_operator_db::MobileNetworkOperator *>
+ candidates_by_name_;
+ const mobile_operator_db::MobileNetworkOperator *current_mno_;
+ const mobile_operator_db::MobileVirtualNetworkOperator *current_mvno_;
+
+ // These fields are the information expected to be populated by this object
+ // after successfully determining the MVNO.
+ std::string uuid_;
+ std::string operator_name_;
+ std::string country_;
+ std::string mccmnc_;
+ std::string sid_;
+ std::string nid_;
+ std::vector<std::string> mccmnc_list_;
+ std::vector<std::string> sid_list_;
+ std::vector<MobileOperatorInfo::LocalizedName> operator_name_list_;
+ ScopedVector<MobileOperatorInfo::MobileAPN> apn_list_;
+ std::vector<MobileOperatorInfo::OnlinePortal> olp_list_;
+ std::vector<mobile_operator_db::OnlinePortal> raw_olp_list_;
+ std::string activation_code_;
+ bool requires_roaming_;
+ // These fields store the data obtained from the Update* methods.
+ // The database information is kept separate from the information gathered
+ // through the Update* methods, because one or the other may be given
+ // precedence in different situations.
+ // Note: For simplicity, we do not allow the user to enforce an empty value
+ // for these variables. So, if |user_mccmnc_| == "", the |mccmnc_| obtained
+ // from the database will be used, even if |user_mccmnc_| was explicitly set
+ // by the user.
+ std::string user_imsi_;
+ std::string user_iccid_;
+ std::string user_mccmnc_;
+ std::string user_sid_;
+ std::string user_nid_;
+ std::string user_operator_name_;
+ bool user_olp_empty_;
+ MobileOperatorInfo::OnlinePortal user_olp_;
+
+ // This must be the last data member of this class.
+ base::WeakPtrFactory<MobileOperatorInfoImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoImpl);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOBILE_OPERATOR_INFO_IMPL_H_
diff --git a/cellular/mobile_operator_info_unittest.cc b/cellular/mobile_operator_info_unittest.cc
new file mode 100644
index 0000000..c865a6e
--- /dev/null
+++ b/cellular/mobile_operator_info_unittest.cc
@@ -0,0 +1,1616 @@
+// Copyright (c) 2014 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/mobile_operator_info.h"
+
+#include <fstream>
+#include <map>
+#include <ostream>
+#include <set>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/macros.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "shill/cellular/mobile_operator_info_impl.h"
+#include "shill/event_dispatcher.h"
+#include "shill/logging.h"
+
+// These files contain binary protobuf definitions used by the following tests
+// inside the namespace ::mobile_operator_db
+#define IN_MOBILE_OPERATOR_INFO_UNITTEST_CC
+#include "shill/mobile_operator_db/test_protos/data_test.h"
+#include "shill/mobile_operator_db/test_protos/init_test_empty_db_init.h"
+#include "shill/mobile_operator_db/test_protos/init_test_multiple_db_init_1.h"
+#include "shill/mobile_operator_db/test_protos/init_test_multiple_db_init_2.h"
+#include "shill/mobile_operator_db/test_protos/init_test_successful_init.h"
+#include "shill/mobile_operator_db/test_protos/main_test.h"
+#undef IN_MOBILE_OPERATOR_INFO_UNITTEST_CC
+
+using base::FilePath;
+using shill::mobile_operator_db::MobileOperatorDB;
+using std::map;
+using std::ofstream;
+using std::set;
+using std::string;
+using std::vector;
+using testing::Mock;
+using testing::Test;
+using testing::Values;
+using testing::WithParamInterface;
+
+// The tests run from the fixture |MobileOperatorInfoMainTest| and
+// |MobileOperatorDataTest| can be run in two modes:
+// - strict event checking: We check that an event is raised for each update
+// to the state of the object.
+// - non-strict event checking: We check that a single event is raised as a
+// result of many updates to the object.
+// The first case corresponds to a very aggressive event loop, that dispatches
+// events as soon as they are posted; the second one corresponds to an
+// over-crowded event loop that only dispatches events just before we verify
+// that events were raised.
+//
+// We use ::testing::WithParamInterface to templatize the test fixtures to do
+// string/non-strict event checking. When writing test cases using these
+// fixtures, use the |Update*|, |ExpectEventCount|, |VerifyEventCount| functions
+// provided by the fixture, and write the test as if event checking is strict.
+//
+// For |MobileOperatorObserverTest|, only the strict event checking case makes
+// sense, so we only instantiate that.
+namespace shill {
+
+namespace {
+
+enum EventCheckingPolicy {
+ kEventCheckingPolicyStrict,
+ kEventCheckingPolicyNonStrict
+};
+
+} // namespace
+
+class MockMobileOperatorInfoObserver : public MobileOperatorInfo::Observer {
+ public:
+ MockMobileOperatorInfoObserver() {}
+ virtual ~MockMobileOperatorInfoObserver() {}
+
+ MOCK_METHOD0(OnOperatorChanged, void());
+};
+
+class MobileOperatorInfoInitTest : public Test {
+ public:
+ MobileOperatorInfoInitTest()
+ : operator_info_(new MobileOperatorInfo(&dispatcher_, "Operator")),
+ operator_info_impl_(operator_info_->impl()) {}
+
+ void TearDown() override {
+ for (const auto &tmp_db_path : tmp_db_paths_) {
+ base::DeleteFile(tmp_db_path, false);
+ }
+ }
+
+ protected:
+ void AddDatabase(const unsigned char database_data[], size_t num_elems) {
+ FilePath tmp_db_path;
+ CHECK(base::CreateTemporaryFile(&tmp_db_path));
+ tmp_db_paths_.push_back(tmp_db_path);
+
+ ofstream tmp_db(tmp_db_path.value(), ofstream::binary);
+ for (size_t i = 0; i < num_elems; ++i) {
+ tmp_db << database_data[i];
+ }
+ tmp_db.close();
+ operator_info_->AddDatabasePath(tmp_db_path);
+ }
+
+ void AssertDatabaseEmpty() {
+ EXPECT_EQ(0, operator_info_impl_->database()->mno_size());
+ EXPECT_EQ(0, operator_info_impl_->database()->imvno_size());
+ }
+
+ const MobileOperatorDB *GetDatabase() {
+ return operator_info_impl_->database();
+ }
+
+ EventDispatcher dispatcher_;
+ vector<FilePath> tmp_db_paths_;
+ std::unique_ptr<MobileOperatorInfo> operator_info_;
+ // Owned by |operator_info_| and tied to its life cycle.
+ MobileOperatorInfoImpl *operator_info_impl_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoInitTest);
+};
+
+TEST_F(MobileOperatorInfoInitTest, FailedInitNoPath) {
+ // - Initialize object with no database paths set
+ // - Verify that initialization fails.
+ operator_info_->ClearDatabasePaths();
+ EXPECT_FALSE(operator_info_->Init());
+ AssertDatabaseEmpty();
+}
+
+TEST_F(MobileOperatorInfoInitTest, FailedInitBadPath) {
+ // - Initialize object with non-existent path.
+ // - Verify that initialization fails.
+ const FilePath database_path("nonexistent.pbf");
+ operator_info_->ClearDatabasePaths();
+ operator_info_->AddDatabasePath(database_path);
+ EXPECT_FALSE(operator_info_->Init());
+ AssertDatabaseEmpty();
+}
+
+TEST_F(MobileOperatorInfoInitTest, FailedInitBadDatabase) {
+ // - Initialize object with malformed database.
+ // - Verify that initialization fails.
+ // TODO(pprabhu): It's hard to get a malformed database in binary format.
+}
+
+TEST_F(MobileOperatorInfoInitTest, EmptyDBInit) {
+ // - Initialize the object with a database file that is empty.
+ // - Verify that initialization succeeds, and that the database is empty.
+ operator_info_->ClearDatabasePaths();
+ // Can't use arraysize on empty array.
+ AddDatabase(mobile_operator_db::init_test_empty_db_init, 0);
+ EXPECT_TRUE(operator_info_->Init());
+ AssertDatabaseEmpty();
+}
+
+TEST_F(MobileOperatorInfoInitTest, SuccessfulInit) {
+ operator_info_->ClearDatabasePaths();
+ AddDatabase(mobile_operator_db::init_test_successful_init,
+ arraysize(mobile_operator_db::init_test_successful_init));
+ EXPECT_TRUE(operator_info_->Init());
+ EXPECT_GT(GetDatabase()->mno_size(), 0);
+ EXPECT_GT(GetDatabase()->imvno_size(), 0);
+}
+
+TEST_F(MobileOperatorInfoInitTest, MultipleDBInit) {
+ // - Initialize the object with two database files.
+ // - Verify that intialization succeeds, and both databases are loaded.
+ operator_info_->ClearDatabasePaths();
+ AddDatabase(mobile_operator_db::init_test_multiple_db_init_1,
+ arraysize(mobile_operator_db::init_test_multiple_db_init_1));
+ AddDatabase(mobile_operator_db::init_test_multiple_db_init_2,
+ arraysize(mobile_operator_db::init_test_multiple_db_init_2));
+ operator_info_->Init();
+ EXPECT_GT(GetDatabase()->mno_size(), 0);
+ EXPECT_GT(GetDatabase()->imvno_size(), 0);
+}
+
+TEST_F(MobileOperatorInfoInitTest, InitWithObserver) {
+ // - Add an Observer.
+ // - Initialize the object with empty database file.
+ // - Verify innitialization succeeds.
+ MockMobileOperatorInfoObserver dumb_observer;
+
+ operator_info_->ClearDatabasePaths();
+ // Can't use arraysize with empty array.
+ AddDatabase(mobile_operator_db::init_test_empty_db_init, 0);
+ operator_info_->AddObserver(&dumb_observer);
+ EXPECT_TRUE(operator_info_->Init());
+}
+
+class MobileOperatorInfoMainTest
+ : public MobileOperatorInfoInitTest,
+ public WithParamInterface<EventCheckingPolicy> {
+ public:
+ MobileOperatorInfoMainTest()
+ : MobileOperatorInfoInitTest(),
+ event_checking_policy_(GetParam()) {}
+
+ virtual void SetUp() {
+ operator_info_->ClearDatabasePaths();
+ AddDatabase(mobile_operator_db::main_test,
+ arraysize(mobile_operator_db::main_test));
+ operator_info_->Init();
+ operator_info_->AddObserver(&observer_);
+ }
+
+ protected:
+ // ///////////////////////////////////////////////////////////////////////////
+ // Helper functions.
+ void VerifyMNOWithUUID(const string &uuid) {
+ EXPECT_TRUE(operator_info_->IsMobileNetworkOperatorKnown());
+ EXPECT_FALSE(operator_info_->IsMobileVirtualNetworkOperatorKnown());
+ EXPECT_EQ(uuid, operator_info_->uuid());
+ }
+
+ void VerifyMVNOWithUUID(const string &uuid) {
+ EXPECT_TRUE(operator_info_->IsMobileNetworkOperatorKnown());
+ EXPECT_TRUE(operator_info_->IsMobileVirtualNetworkOperatorKnown());
+ EXPECT_EQ(uuid, operator_info_->uuid());
+ }
+
+ void VerifyNoMatch() {
+ EXPECT_FALSE(operator_info_->IsMobileNetworkOperatorKnown());
+ EXPECT_FALSE(operator_info_->IsMobileVirtualNetworkOperatorKnown());
+ EXPECT_EQ("", operator_info_->uuid());
+ }
+
+ void ExpectEventCount(int count) {
+ // In case we're running in the non-strict event checking mode, we only
+ // expect one overall event to be raised for all the updates.
+ if (event_checking_policy_ == kEventCheckingPolicyNonStrict) {
+ count = (count > 0) ? 1 : 0;
+ }
+ EXPECT_CALL(observer_, OnOperatorChanged()).Times(count);
+ }
+
+ void VerifyEventCount() {
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(&observer_);
+ }
+
+ void ResetOperatorInfo() {
+ operator_info_->Reset();
+ // Eat up any events caused by |Reset|.
+ dispatcher_.DispatchPendingEvents();
+ VerifyNoMatch();
+ }
+
+ // Use these wrappers to send updates to |operator_info_|. These wrappers
+ // optionally run the dispatcher if we want strict checking of the number of
+ // events raised.
+ void UpdateMCCMNC(const std::string &mccmnc) {
+ operator_info_->UpdateMCCMNC(mccmnc);
+ DispatchPendingEventsIfStrict();
+ }
+
+ void UpdateSID(const std::string &sid) {
+ operator_info_->UpdateSID(sid);
+ DispatchPendingEventsIfStrict();
+ }
+
+ void UpdateIMSI(const std::string &imsi) {
+ operator_info_->UpdateIMSI(imsi);
+ DispatchPendingEventsIfStrict();
+ }
+
+ void UpdateICCID(const std::string &iccid) {
+ operator_info_->UpdateICCID(iccid);
+ DispatchPendingEventsIfStrict();
+ }
+
+ void UpdateNID(const std::string &nid) {
+ operator_info_->UpdateNID(nid);
+ DispatchPendingEventsIfStrict();
+ }
+
+ void UpdateOperatorName(const std::string &operator_name) {
+ operator_info_->UpdateOperatorName(operator_name);
+ DispatchPendingEventsIfStrict();
+ }
+
+ void UpdateOnlinePortal(const std::string &url,
+ const std::string &method,
+ const std::string &post_data) {
+ operator_info_->UpdateOnlinePortal(url, method, post_data);
+ DispatchPendingEventsIfStrict();
+ }
+
+ void DispatchPendingEventsIfStrict() {
+ if (event_checking_policy_ == kEventCheckingPolicyStrict) {
+ dispatcher_.DispatchPendingEvents();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////////
+ // Data.
+ MockMobileOperatorInfoObserver observer_;
+ const EventCheckingPolicy event_checking_policy_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoMainTest);
+};
+
+TEST_P(MobileOperatorInfoMainTest, InitialConditions) {
+ // - Initialize a new object.
+ // - Verify that all initial values of properties are reasonable.
+ EXPECT_FALSE(operator_info_->IsMobileNetworkOperatorKnown());
+ EXPECT_FALSE(operator_info_->IsMobileVirtualNetworkOperatorKnown());
+ EXPECT_TRUE(operator_info_->uuid().empty());
+ EXPECT_TRUE(operator_info_->operator_name().empty());
+ EXPECT_TRUE(operator_info_->country().empty());
+ EXPECT_TRUE(operator_info_->mccmnc().empty());
+ EXPECT_TRUE(operator_info_->sid().empty());
+ EXPECT_TRUE(operator_info_->nid().empty());
+ EXPECT_TRUE(operator_info_->mccmnc_list().empty());
+ EXPECT_TRUE(operator_info_->sid_list().empty());
+ EXPECT_TRUE(operator_info_->operator_name_list().empty());
+ EXPECT_TRUE(operator_info_->apn_list().empty());
+ EXPECT_TRUE(operator_info_->olp_list().empty());
+ EXPECT_TRUE(operator_info_->activation_code().empty());
+ EXPECT_FALSE(operator_info_->requires_roaming());
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNC) {
+ // message: Has an MNO with no MVNO.
+ // match by: MCCMNC.
+ // verify: Observer event, uuid.
+
+ ExpectEventCount(0);
+ UpdateMCCMNC("101999"); // No match.
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("101001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid101");
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("101999");
+ VerifyEventCount();
+ VerifyNoMatch();
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCMultipleMCCMNCOptions) {
+ // message: Has an MNO with no MCCMNC.
+ // match by: One of the MCCMNCs of the multiple ones in the MNO.
+ // verify: Observer event, uuid.
+ ExpectEventCount(1);
+ UpdateMCCMNC("102002");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid102");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCMultipleMNOOptions) {
+ // message: Two messages with the same MCCMNC.
+ // match by: Both MNOs matched, one is earmarked.
+ // verify: The earmarked MNO is picked.
+ ExpectEventCount(1);
+ UpdateMCCMNC("124001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid124002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByOperatorName) {
+ // message: Has an MNO with no MVNO.
+ // match by: OperatorName.
+ // verify: Observer event, uuid.
+ ExpectEventCount(0);
+ UpdateOperatorName("name103999"); // No match.
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateOperatorName("name103");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid103");
+
+ ExpectEventCount(1);
+ UpdateOperatorName("name103999"); // No match.
+ VerifyEventCount();
+ VerifyNoMatch();
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameMultipleMNOOptions) {
+ // message: Two messages with the same operator name.
+ // match by: Both MNOs matched, one is earmarked.
+ // verify: The earmarked MNO is picked.
+ ExpectEventCount(1);
+ UpdateOperatorName("name125001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid125002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameAggressiveMatch) {
+ // These network operators match by name but only after normalizing the names.
+ // Both the name from the database and the name provided to
+ // |UpdateOperatoName| must be normalized for this test to pass.
+ ExpectEventCount(1);
+ UpdateOperatorName("name126001 casedoesnotmatch");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid126001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(1);
+ UpdateOperatorName("name126002 CaseStillDoesNotMatch");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid126002");
+
+ ResetOperatorInfo();
+ ExpectEventCount(1);
+ UpdateOperatorName("name126003GiveMeMoreSpace");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid126003");
+
+ ResetOperatorInfo();
+ ExpectEventCount(1);
+ UpdateOperatorName("name126004 Too Much Air Here");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid126004");
+
+ ResetOperatorInfo();
+ ExpectEventCount(1);
+ UpdateOperatorName("näméwithNon-Äσ¢ii");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid126005");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameWithLang) {
+ // message: Has an MNO with no MVNO.
+ // match by: OperatorName.
+ // verify: Observer event, fields.
+ ExpectEventCount(1);
+ UpdateOperatorName("name105");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid105");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameMultipleNameOptions) {
+ // message: Has an MNO with no MVNO.
+ // match by: OperatorName, one of the multiple present in the MNO.
+ // verify: Observer event, fields.
+ ExpectEventCount(1);
+ UpdateOperatorName("name104002");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid104");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCAndOperatorName) {
+ // message: Has MNOs with no MVNO.
+ // match by: MCCMNC finds two candidates (first one is chosen), Name narrows
+ // down to one.
+ // verify: Observer event, fields.
+ // This is merely a MCCMNC update.
+ ExpectEventCount(1);
+ UpdateMCCMNC("106001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid106001");
+
+ ExpectEventCount(1);
+ UpdateOperatorName("name106002");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid106002");
+
+ ResetOperatorInfo();
+ // Try updates in reverse order.
+ ExpectEventCount(1);
+ UpdateOperatorName("name106001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid106001");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameAndMCCMNC) {
+ // message: Has MNOs with no MVNO.
+ // match by: OperatorName finds two (first one is chosen), MCCMNC narrows down
+ // to one.
+ // verify: Observer event, fields.
+ // This is merely an OperatorName update.
+ ExpectEventCount(1);
+ UpdateOperatorName("name107");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid107001");
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("107002");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid107002");
+
+ ResetOperatorInfo();
+ // Try updates in reverse order.
+ ExpectEventCount(1);
+ UpdateMCCMNC("107001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid107001");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCOverridesOperatorName) {
+ // message: Has MNOs with no MVNO.
+ // match by: First MCCMNC finds one. Then, OperatorName matches another.
+ // verify: MCCMNC match prevails. No change on OperatorName update.
+ ExpectEventCount(1);
+ UpdateMCCMNC("108001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid108001");
+
+ // An event is sent for the updated OperatorName.
+ ExpectEventCount(1);
+ UpdateOperatorName("name108002"); // Does not match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid108001");
+ // OperatorName from the database is given preference over the user supplied
+ // one.
+ EXPECT_EQ("name108001", operator_info_->operator_name());
+
+ ResetOperatorInfo();
+ // message: Same as above.
+ // match by: First OperatorName finds one, then MCCMNC overrides it.
+ // verify: Two events, MCCMNC one overriding the OperatorName one.
+ ExpectEventCount(1);
+ UpdateOperatorName("name108001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid108001");
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("108002");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid108002");
+ EXPECT_EQ("name108002", operator_info_->operator_name());
+
+ // message: Same as above.
+ // match by: First a *wrong* MCCMNC update, followed by the correct Name
+ // update.
+ // verify: No MNO, since MCCMNC is given precedence.
+ ResetOperatorInfo();
+ ExpectEventCount(0);
+ UpdateMCCMNC("108999"); // Does not match.
+ UpdateOperatorName("name108001");
+ VerifyEventCount();
+ VerifyNoMatch();
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByIMSI) {
+ // message: Has MNO with no MVNO.
+ // match by: MCCMNC part of IMSI of length 5 / 6.
+ ExpectEventCount(0);
+ UpdateIMSI("109"); // Too short.
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(0);
+ UpdateIMSI("109995432154321"); // No match.
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ResetOperatorInfo();
+ // Short MCCMNC match.
+ ExpectEventCount(1);
+ UpdateIMSI("109015432154321"); // First 5 digits match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid10901");
+
+ ResetOperatorInfo();
+ // Long MCCMNC match.
+ ExpectEventCount(1);
+ UpdateIMSI("10900215432154321"); // First 6 digits match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid109002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCOverridesIMSI) {
+ // message: Has MNOs with no MVNO.
+ // match by: One matches MCCMNC, then one matches a different MCCMNC substring
+ // of IMSI
+ // verify: Observer event for the first match, all fields. Second Update
+ // ignored.
+ ExpectEventCount(1);
+ UpdateMCCMNC("110001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid110001");
+
+ // MNO remains unchanged on a mismatched IMSI update.
+ ExpectEventCount(0);
+ UpdateIMSI("1100025432154321"); // First 6 digits match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid110001");
+
+ // MNO remains uncnaged on an invalid IMSI update.
+ ExpectEventCount(0);
+ UpdateIMSI("1100035432154321"); // Prefix does not match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid110001");
+
+ ExpectEventCount(0);
+ UpdateIMSI("110"); // Too small.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid110001");
+
+ ResetOperatorInfo();
+ // Same as above, but this time, match with IMSI, followed by a contradictory
+ // MCCMNC update. The second update should override the first one.
+ ExpectEventCount(1);
+ UpdateIMSI("1100025432154321"); // First 6 digits match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid110002");
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("110001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid110001");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOUchangedBySecondaryUpdates) {
+ // This test verifies that only some updates affect the MNO.
+ // message: Has MNOs with no MVNO.
+ // match by: First matches the MCCMNC. Later, MNOs with a different MCCMNC
+ // matchs the given SID, NID, ICCID.
+ // verify: Only one Observer event, on the first MCCMNC match.
+ ExpectEventCount(1);
+ UpdateMCCMNC("111001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid111001");
+
+ ExpectEventCount(1); // NID change event.
+ UpdateNID("111202");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid111001");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNODefaultMatch) {
+ // message: MNO with one MVNO (no filter).
+ // match by: MNO matches by MCCMNC.
+ // verify: Observer event for MVNO match. Uuid match the MVNO.
+ // second update: ICCID.
+ // verify: No observer event, match remains unchanged.
+ ExpectEventCount(1);
+ UpdateMCCMNC("112001");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid112002");
+
+ ExpectEventCount(0);
+ UpdateICCID("112002");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid112002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNONameMatch) {
+ // message: MNO with one MVNO (name filter).
+ // match by: MNO matches by MCCMNC,
+ // MVNO fails to match by fist name update,
+ // then MVNO matches by name.
+ // verify: Two Observer events: MNO followed by MVNO.
+ ExpectEventCount(1);
+ UpdateMCCMNC("113001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid113001");
+
+ ExpectEventCount(1);
+ UpdateOperatorName("name113999"); // No match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid113001");
+ // Name from the database is given preference.
+ EXPECT_EQ("name113001", operator_info_->operator_name());
+
+ ExpectEventCount(1);
+ UpdateOperatorName("name113002");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid113002");
+ EXPECT_EQ("name113002", operator_info_->operator_name());
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNONameMalformedRegexMatch) {
+ // message: MNO with one MVNO (name filter with a malformed regex).
+ // match by: MNO matches by MCCMNC.
+ // MVNO does not match
+ ExpectEventCount(2);
+ UpdateMCCMNC("114001");
+ UpdateOperatorName("name[");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid114001");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNONameSubexpressionRegexMatch) {
+ // message: MNO with one MVNO (name filter with simple regex).
+ // match by: MNO matches by MCCMNC.
+ // MVNO does not match with a name whose subexpression matches the
+ // regex.
+ ExpectEventCount(2); // One event for just the name update.
+ UpdateMCCMNC("115001");
+ UpdateOperatorName("name115_ExtraCrud");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid115001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2); // One event for just the name update.
+ UpdateMCCMNC("115001");
+ UpdateOperatorName("ExtraCrud_name115");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid115001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2); // One event for just the name update.
+ UpdateMCCMNC("115001");
+ UpdateOperatorName("ExtraCrud_name115_ExtraCrud");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid115001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2); // One event for just the name update.
+ UpdateMCCMNC("115001");
+ UpdateOperatorName("name_ExtraCrud_115");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid115001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("115001");
+ UpdateOperatorName("name115");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid115002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNONameRegexMatch) {
+ // message: MNO with one MVNO (name filter with non-trivial regex).
+ // match by: MNO matches by MCCMNC.
+ // MVNO fails to match several times with different strings.
+ // MVNO matches several times with different values.
+
+ // Make sure we're not taking the regex literally!
+ ExpectEventCount(2);
+ UpdateMCCMNC("116001");
+ UpdateOperatorName("name[a-zA-Z_]*116[0-9]{0,3}");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid116001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("116001");
+ UpdateOperatorName("name[a-zA-Z_]116[0-9]");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid116001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("116001");
+ UpdateOperatorName("nameb*1167");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid116001");
+
+ // Success!
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("116001");
+ UpdateOperatorName("name116");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid116002");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("116001");
+ UpdateOperatorName("nameSomeWord116");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid116002");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("116001");
+ UpdateOperatorName("name116567");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid116002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNONameMatchMultipleFilters) {
+ // message: MNO with one MVNO with two name filters.
+ // match by: MNO matches by MCCMNC.
+ // MVNO first fails on the second filter alone.
+ // MVNO fails on the first filter alone.
+ // MVNO matches on both filters.
+ ExpectEventCount(2);
+ UpdateMCCMNC("117001");
+ UpdateOperatorName("nameA_crud");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid117001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("117001");
+ UpdateOperatorName("crud_nameB");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid117001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("117001");
+ UpdateOperatorName("crud_crud");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid117001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("117001");
+ UpdateOperatorName("nameA_nameB");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid117002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNOIMSIMatch) {
+ // message: MNO with one MVNO (imsi filter).
+ // match by: MNO matches by MCCMNC,
+ // MVNO fails to match by fist imsi update,
+ // then MVNO matches by imsi.
+ // verify: Two Observer events: MNO followed by MVNO.
+ ExpectEventCount(1);
+ UpdateMCCMNC("118001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid118001");
+
+ ExpectEventCount(0);
+ UpdateIMSI("1180011234512345"); // No match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid118001");
+
+ ExpectEventCount(1);
+ UpdateIMSI("1180015432154321");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid118002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNOICCIDMatch) {
+ // message: MNO with one MVNO (iccid filter).
+ // match by: MNO matches by MCCMNC,
+ // MVNO fails to match by fist iccid update,
+ // then MVNO matches by iccid.
+ // verify: Two Observer events: MNO followed by MVNO.
+ ExpectEventCount(1);
+ UpdateMCCMNC("119001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid119001");
+
+ ExpectEventCount(0);
+ UpdateICCID("119987654321"); // No match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid119001");
+
+ ExpectEventCount(1);
+ UpdateICCID("119123456789");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid119002");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNOSIDMatch) {
+ // message: MNO with one MVNO (sid filter).
+ // match by: MNO matches by SID,
+ // MVNO fails to match by fist sid update,
+ // then MVNO matches by sid.
+ // verify: Two Observer events: MNO followed by MVNO.
+ ExpectEventCount(0);
+ UpdateSID("120999"); // No match.
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateSID("120001"); // Only MNO matches.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid120001");
+ EXPECT_EQ("120001", operator_info_->sid());
+
+ ExpectEventCount(1);
+ UpdateSID("120002"); // MVNO matches as well.
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid120002");
+ EXPECT_EQ("120002", operator_info_->sid());
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNOAllMatch) {
+ // message: MNO with following MVNOS:
+ // - one with no filter.
+ // - one with name filter.
+ // - one with imsi filter.
+ // - one with iccid filter.
+ // - one with name and iccid filter.
+ // verify:
+ // - initial MCCMNC matches the default MVNO directly (not MNO)
+ // - match each of the MVNOs in turn.
+ // - give super set information that does not match any MVNO correctly,
+ // verify that the MNO matches.
+ ExpectEventCount(1);
+ UpdateMCCMNC("121001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid121001");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("121001");
+ UpdateOperatorName("name121003");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid121003");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("121001");
+ UpdateIMSI("1210045432154321");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid121004");
+
+ ResetOperatorInfo();
+ ExpectEventCount(2);
+ UpdateMCCMNC("121001");
+ UpdateICCID("121005123456789");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid121005");
+
+ ResetOperatorInfo();
+ ExpectEventCount(3);
+ UpdateMCCMNC("121001");
+ UpdateOperatorName("name121006");
+ VerifyMNOWithUUID("uuid121001");
+ UpdateICCID("121006123456789");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid121006");
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNOMatchAndMismatch) {
+ // message: MNO with one MVNO with name filter.
+ // match by: MNO matches by MCCMNC
+ // MVNO matches by name.
+ // Second name update causes the MVNO to not match again.
+ ExpectEventCount(1);
+ UpdateMCCMNC("113001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid113001");
+
+ ExpectEventCount(1);
+ UpdateOperatorName("name113002");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid113002");
+ EXPECT_EQ("name113002", operator_info_->operator_name());
+
+ ExpectEventCount(1);
+ UpdateOperatorName("name113999"); // No match.
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid113001");
+ // Name from database is given preference.
+ EXPECT_EQ("name113001", operator_info_->operator_name());
+}
+
+TEST_P(MobileOperatorInfoMainTest, MVNOMatchAndReset) {
+ // message: MVNO with name filter.
+ // verify;
+ // - match MVNO by name.
+ // - Reset object, verify Observer event, and not match.
+ // - match MVNO by name again.
+ ExpectEventCount(1);
+ UpdateMCCMNC("113001");
+ VerifyEventCount();
+ ExpectEventCount(1);
+ VerifyMNOWithUUID("uuid113001");
+ UpdateOperatorName("name113002");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid113002");
+ EXPECT_EQ("name113002", operator_info_->operator_name());
+
+ ExpectEventCount(1);
+ operator_info_->Reset();
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("113001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid113001");
+ ExpectEventCount(1);
+ UpdateOperatorName("name113002");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid113002");
+ EXPECT_EQ("name113002", operator_info_->operator_name());
+}
+
+// Here, we rely on our knowledge about the implementation: The SID and MCCMNC
+// updates follow the same code paths, and so we can get away with not testing
+// all the scenarios we test above for MCCMNC. Instead, we only do basic testing
+// to make sure that SID upates operator as MCCMNC updates do.
+TEST_P(MobileOperatorInfoMainTest, MNOBySID) {
+ // message: Has an MNO with no MVNO.
+ // match by: SID.
+ // verify: Observer event, uuid.
+
+ ExpectEventCount(0);
+ UpdateSID("1229"); // No match.
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateSID("1221");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid1221");
+
+ ExpectEventCount(1);
+ UpdateSID("1229"); // No Match.
+ VerifyEventCount();
+ VerifyNoMatch();
+}
+
+TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCAndSID) {
+ // message: Has an MNO with no MVNO.
+ // match by: SID / MCCMNC alternately.
+ // verify: Observer event, uuid.
+
+ ExpectEventCount(0);
+ UpdateMCCMNC("123999"); // NO match.
+ UpdateSID("1239"); // No match.
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("123001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid123001");
+
+ ExpectEventCount(1);
+ operator_info_->Reset();
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateSID("1232");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid1232");
+
+ ExpectEventCount(1);
+ operator_info_->Reset();
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("123001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid123001");
+}
+
+class MobileOperatorInfoDataTest : public MobileOperatorInfoMainTest {
+ public:
+ MobileOperatorInfoDataTest() : MobileOperatorInfoMainTest() {}
+
+ // Same as MobileOperatorInfoMainTest, except that the database used is
+ // different.
+ virtual void SetUp() {
+ operator_info_->ClearDatabasePaths();
+ AddDatabase(mobile_operator_db::data_test,
+ arraysize(mobile_operator_db::data_test));
+ operator_info_->Init();
+ operator_info_->AddObserver(&observer_);
+ }
+
+ protected:
+ // This is a function that does a best effort verification of the information
+ // that is obtained from the database by the MobileOperatorInfo object against
+ // expectations stored in the form of data members in this class.
+ // This is not a full proof check. In particular:
+ // - It is unspecified in some case which of the values from a list is
+ // exposed as a property. For example, at best, we can check that |sid| is
+ // non-empty.
+ // - It is not robust to "" as property values at times.
+ void VerifyDatabaseData() {
+ EXPECT_EQ(country_, operator_info_->country());
+ EXPECT_EQ(requires_roaming_, operator_info_->requires_roaming());
+ EXPECT_EQ(activation_code_, operator_info_->activation_code());
+
+ EXPECT_EQ(mccmnc_list_.size(), operator_info_->mccmnc_list().size());
+ set<string> mccmnc_set(operator_info_->mccmnc_list().begin(),
+ operator_info_->mccmnc_list().end());
+ for (const auto &mccmnc : mccmnc_list_) {
+ EXPECT_TRUE(mccmnc_set.find(mccmnc) != mccmnc_set.end());
+ }
+ if (mccmnc_list_.size() > 0) {
+ // It is not specified which entry will be chosen, but mccmnc() must be
+ // non empty.
+ EXPECT_FALSE(operator_info_->mccmnc().empty());
+ }
+
+ VerifyNameListsMatch(operator_name_list_,
+ operator_info_->operator_name_list());
+
+ // This comparison breaks if two apns have the same |apn| field.
+ EXPECT_EQ(apn_list_.size(), operator_info_->apn_list().size());
+ map<string, const MobileOperatorInfo::MobileAPN *> mobile_apns;
+ for (const auto &apn_node : operator_info_->apn_list()) {
+ mobile_apns[apn_node->apn] = apn_node;
+ }
+ for (const auto &apn_lhs : apn_list_) {
+ ASSERT_TRUE(mobile_apns.find(apn_lhs->apn) != mobile_apns.end());
+ const auto &apn_rhs = mobile_apns[apn_lhs->apn];
+ // Only comparing apn, name, username, password.
+ EXPECT_EQ(apn_lhs->apn, apn_rhs->apn);
+ EXPECT_EQ(apn_lhs->username, apn_rhs->username);
+ EXPECT_EQ(apn_lhs->password, apn_rhs->password);
+ VerifyNameListsMatch(apn_lhs->operator_name_list,
+ apn_rhs->operator_name_list);
+ }
+
+ EXPECT_EQ(olp_list_.size(), operator_info_->olp_list().size());
+ // This comparison breaks if two OLPs have the same |url|.
+ map<string, MobileOperatorInfo::OnlinePortal> olps;
+ for (const auto &olp : operator_info_->olp_list()) {
+ olps[olp.url] = olp;
+ }
+ for (const auto &olp : olp_list_) {
+ ASSERT_TRUE(olps.find(olp.url) != olps.end());
+ const auto &olp_rhs = olps[olp.url];
+ EXPECT_EQ(olp.method, olp_rhs.method);
+ EXPECT_EQ(olp.post_data, olp_rhs.post_data);
+ }
+
+ EXPECT_EQ(sid_list_.size(), operator_info_->sid_list().size());
+ set<string> sid_set(operator_info_->sid_list().begin(),
+ operator_info_->sid_list().end());
+ for (const auto &sid : sid_list_) {
+ EXPECT_TRUE(sid_set.find(sid) != sid_set.end());
+ }
+ if (sid_list_.size() > 0) {
+ // It is not specified which entry will be chosen, but |sid()| must be
+ // non-empty.
+ EXPECT_FALSE(operator_info_->sid().empty());
+ }
+ }
+
+ // This function does some extra checks for the user data that can not be done
+ // when data is obtained from the database.
+ void VerifyUserData() {
+ EXPECT_EQ(sid_, operator_info_->sid());
+ }
+
+ void VerifyNameListsMatch(
+ const vector<MobileOperatorInfo::LocalizedName> &operator_name_list_lhs,
+ const vector<MobileOperatorInfo::LocalizedName> &operator_name_list_rhs) {
+ // This comparison breaks if two localized names have the same |name|.
+ map<string, MobileOperatorInfo::LocalizedName> localized_names;
+ for (const auto &localized_name : operator_name_list_rhs) {
+ localized_names[localized_name.name] = localized_name;
+ }
+ for (const auto &localized_name : operator_name_list_lhs) {
+ EXPECT_TRUE(localized_names.find(localized_name.name) !=
+ localized_names.end());
+ EXPECT_EQ(localized_name.language,
+ localized_names[localized_name.name].language);
+ }
+ }
+
+ // Use this function to pre-popluate all the data members of this object with
+ // values matching the MNO for the database in |data_test.prototxt|.
+ void PopulateMNOData() {
+ country_ = "us";
+ requires_roaming_ = true;
+ activation_code_ = "open sesame";
+
+ mccmnc_list_.clear();
+ mccmnc_list_.push_back("200001");
+ mccmnc_list_.push_back("200002");
+ mccmnc_list_.push_back("200003");
+
+ operator_name_list_.clear();
+ operator_name_list_.push_back({"name200001", "en"});
+ operator_name_list_.push_back({"name200002", ""});
+
+ apn_list_.clear();
+ MobileOperatorInfo::MobileAPN *apn;
+ apn = new MobileOperatorInfo::MobileAPN();
+ apn->apn = "test@test.com";
+ apn->username = "testuser";
+ apn->password = "is_public_boohoohoo";
+ apn->operator_name_list.push_back({"name200003", "hi"});
+ apn_list_.push_back(apn); // Takes ownership.
+
+ olp_list_.clear();
+ olp_list_.push_back({"some@random.com", "POST", "random_data"});
+
+ sid_list_.clear();
+ sid_list_.push_back("200123");
+ sid_list_.push_back("200234");
+ sid_list_.push_back("200345");
+ }
+
+ // Use this function to pre-populate all the data members of this object with
+ // values matching the MVNO for the database in |data_test.prototext|.
+ void PopulateMVNOData() {
+ country_ = "ca";
+ requires_roaming_ = false;
+ activation_code_ = "khul ja sim sim";
+
+ mccmnc_list_.clear();
+ mccmnc_list_.push_back("200001");
+ mccmnc_list_.push_back("200102");
+
+ operator_name_list_.clear();
+ operator_name_list_.push_back({"name200101", "en"});
+ operator_name_list_.push_back({"name200102", ""});
+
+ apn_list_.clear();
+ MobileOperatorInfo::MobileAPN *apn;
+ apn = new MobileOperatorInfo::MobileAPN();
+ apn->apn = "test2@test.com";
+ apn->username = "testuser2";
+ apn->password = "is_public_boohoohoo_too";
+ apn_list_.push_back(apn); // Takes ownership.
+
+ olp_list_.clear();
+ olp_list_.push_back({"someother@random.com", "GET", ""});
+
+ sid_list_.clear();
+ sid_list_.push_back("200345");
+ }
+
+ // Data to be verified against the database.
+ string country_;
+ bool requires_roaming_;
+ string activation_code_;
+ vector<string> mccmnc_list_;
+ vector<MobileOperatorInfo::LocalizedName> operator_name_list_;
+ ScopedVector<MobileOperatorInfo::MobileAPN> apn_list_;
+ vector<MobileOperatorInfo::OnlinePortal> olp_list_;
+ vector<string> sid_list_;
+
+ // Extra data to be verified only against user updates.
+ string sid_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoDataTest);
+};
+
+
+TEST_P(MobileOperatorInfoDataTest, MNODetailedInformation) {
+ // message: MNO with all the information filled in.
+ // match by: MNO matches by MCCMNC
+ // verify: All information is correctly loaded.
+ ExpectEventCount(1);
+ UpdateMCCMNC("200001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid200001");
+
+ PopulateMNOData();
+ VerifyDatabaseData();
+}
+
+TEST_P(MobileOperatorInfoDataTest, MVNOInheritsInformation) {
+ // message: MVNO with name filter.
+ // verify: All the missing fields are carried over to the MVNO from MNO.
+ ExpectEventCount(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200201");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid200201");
+
+ PopulateMNOData();
+ VerifyDatabaseData();
+}
+
+TEST_P(MobileOperatorInfoDataTest, MVNOOverridesInformation) {
+ // match by: MNO matches by MCCMNC, MVNO by name.
+ // verify: All information is correctly loaded.
+ // The MVNO in this case overrides the information provided by MNO.
+ ExpectEventCount(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid200101");
+
+ PopulateMVNOData();
+ VerifyDatabaseData();
+}
+
+TEST_P(MobileOperatorInfoDataTest, NoUpdatesBeforeMNOMatch) {
+ // message: MVNO.
+ // - do not match MNO with mccmnc/name
+ // - on different updates, verify no events.
+ ExpectEventCount(0);
+ UpdateMCCMNC("200999"); // No match.
+ UpdateOperatorName("name200001"); // matches MNO
+ UpdateOperatorName("name200101"); // matches MVNO filter.
+ UpdateSID("200999"); // No match.
+ VerifyEventCount();
+ VerifyNoMatch();
+}
+
+TEST_P(MobileOperatorInfoDataTest, UserUpdatesOverrideMVNO) {
+ // - match MVNO.
+ // - send updates to properties and verify events are raised and values of
+ // updated properties override the ones provided by the database.
+ string imsi {"2009991234512345"};
+ string iccid {"200999123456789"};
+ string olp_url {"url@url.com"};
+ string olp_method {"POST"};
+ string olp_post_data {"data"};
+
+ // Determine MVNO.
+ ExpectEventCount(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid200101");
+
+ // Send updates.
+ ExpectEventCount(1);
+ UpdateOnlinePortal(olp_url, olp_method, olp_post_data);
+ UpdateIMSI(imsi);
+ // No event raised because imsi is not exposed.
+ UpdateICCID(iccid);
+ // No event raised because ICCID is not exposed.
+
+ VerifyEventCount();
+
+ // Update our expectations.
+ PopulateMVNOData();
+ olp_list_.push_back({olp_url, olp_method, olp_post_data});
+
+ VerifyDatabaseData();
+}
+
+TEST_P(MobileOperatorInfoDataTest, CachedUserUpdatesOverrideMVNO) {
+ // message: MVNO.
+ // - First send updates that don't identify an MNO.
+ // - Then identify an MNO and MVNO.
+ // - verify that all the earlier updates are cached, and override the MVNO
+ // information.
+ string imsi {"2009991234512345"};
+ string iccid {"200999123456789"};
+ string sid {"200999"};
+ string olp_url {"url@url.com"};
+ string olp_method {"POST"};
+ string olp_post_data {"data"};
+
+ // Send updates.
+ ExpectEventCount(0);
+ UpdateSID(sid);
+ UpdateOnlinePortal(olp_url, olp_method, olp_post_data);
+ UpdateIMSI(imsi);
+ UpdateICCID(iccid);
+ VerifyEventCount();
+
+ // Determine MVNO.
+ ExpectEventCount(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid200101");
+
+ // Update our expectations.
+ PopulateMVNOData();
+ sid_ = sid;
+ sid_list_.push_back(sid);
+ olp_list_.push_back({olp_url, olp_method, olp_post_data});
+
+ VerifyDatabaseData();
+ VerifyUserData();
+}
+
+TEST_P(MobileOperatorInfoDataTest, RedundantUserUpdatesMVNO) {
+ // - match MVNO.
+ // - send redundant updates to properties.
+ // - Verify no events, no updates to properties.
+
+ // Identify MVNO.
+ ExpectEventCount(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid200101");
+
+ // Send redundant updates.
+ // TODO(pprabhu)
+ // |UpdateOnlinePortal| leads to an event because this is the first time this
+ // value are set *by the user*. Although the values from the database were the
+ // same, we did not use those values for filters. It would be ideal to not
+ // raise these redundant events (since no public information about the object
+ // changed), but I haven't invested in doing so yet.
+ ExpectEventCount(1);
+ UpdateOperatorName(operator_info_->operator_name());
+ UpdateOnlinePortal("someother@random.com", "GET", "");
+ VerifyEventCount();
+ PopulateMVNOData();
+ VerifyDatabaseData();
+}
+
+TEST_P(MobileOperatorInfoDataTest, RedundantCachedUpdatesMVNO) {
+ // message: MVNO.
+ // - First send updates that don't identify MVNO, but match the data.
+ // - Then idenityf an MNO and MVNO.
+ // - verify that redundant information occurs only once.
+
+ // Send redundant updates.
+ ExpectEventCount(2);
+ UpdateSID(operator_info_->sid());
+ UpdateOperatorName(operator_info_->operator_name());
+ UpdateOnlinePortal("someother@random.com", "GET", "");
+
+ // Identify MVNO.
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid200101");
+
+ PopulateMVNOData();
+ VerifyDatabaseData();
+}
+
+TEST_P(MobileOperatorInfoDataTest, ResetClearsInformation) {
+ // Repeatedly reset the object and check M[V]NO identification and data.
+ ExpectEventCount(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200201");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid200201");
+ PopulateMNOData();
+ VerifyDatabaseData();
+
+ ExpectEventCount(1);
+ operator_info_->Reset();
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyEventCount();
+ VerifyMVNOWithUUID("uuid200101");
+ PopulateMVNOData();
+ VerifyDatabaseData();
+
+ ExpectEventCount(1);
+ operator_info_->Reset();
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("200001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid200001");
+ PopulateMNOData();
+ VerifyDatabaseData();
+}
+
+TEST_P(MobileOperatorInfoDataTest, FilteredOLP) {
+ // We only check basic filter matching, using the fact that the regex matching
+ // code is shared with the MVNO filtering, and is already well tested.
+ // (1) None of the filters match.
+ ExpectEventCount(1);
+ UpdateMCCMNC("200001");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid200001");
+
+ ASSERT_EQ(1, operator_info_->olp_list().size());
+ // Just check that the filtered OLPs are not in the list.
+ EXPECT_NE("olp@mccmnc", operator_info_->olp_list()[0].url);
+ EXPECT_NE("olp@sid", operator_info_->olp_list()[0].url);
+
+ // (2) MCCMNC filter matches.
+ ExpectEventCount(1);
+ operator_info_->Reset();
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateMCCMNC("200003");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid200001");
+
+ ASSERT_EQ(2, operator_info_->olp_list().size());
+ EXPECT_NE("olp@sid", operator_info_->olp_list()[0].url);
+ bool found_olp_by_mccmnc = false;
+ for (const auto &olp : operator_info_->olp_list()) {
+ found_olp_by_mccmnc |= ("olp@mccmnc" == olp.url);
+ }
+ EXPECT_TRUE(found_olp_by_mccmnc);
+
+ // (3) SID filter matches.
+ ExpectEventCount(1);
+ operator_info_->Reset();
+ VerifyEventCount();
+ VerifyNoMatch();
+
+ ExpectEventCount(1);
+ UpdateSID("200345");
+ VerifyEventCount();
+ VerifyMNOWithUUID("uuid200001");
+
+ ASSERT_EQ(2, operator_info_->olp_list().size());
+ EXPECT_NE("olp@mccmnc", operator_info_->olp_list()[0].url);
+ bool found_olp_by_sid = false;
+ for (const auto &olp : operator_info_->olp_list()) {
+ found_olp_by_sid |= ("olp@sid" == olp.url);
+ }
+ EXPECT_TRUE(found_olp_by_sid);
+}
+
+class MobileOperatorInfoObserverTest : public MobileOperatorInfoMainTest {
+ public:
+ MobileOperatorInfoObserverTest() : MobileOperatorInfoMainTest() {}
+
+ // Same as |MobileOperatorInfoMainTest::SetUp|, except that we don't add a
+ // default observer.
+ virtual void SetUp() {
+ operator_info_->ClearDatabasePaths();
+ AddDatabase(mobile_operator_db::data_test,
+ arraysize(mobile_operator_db::data_test));
+ operator_info_->Init();
+ }
+
+ protected:
+ // ///////////////////////////////////////////////////////////////////////////
+ // Data.
+ MockMobileOperatorInfoObserver second_observer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoObserverTest);
+};
+
+TEST_P(MobileOperatorInfoObserverTest, NoObserver) {
+ // - Don't add any observers, and then cause an MVNO update to occur.
+ // - Verify no crash.
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+}
+
+TEST_P(MobileOperatorInfoObserverTest, MultipleObservers) {
+ // - Add two observers, and then cause an MVNO update to occur.
+ // - Verify both observers are notified.
+ operator_info_->AddObserver(&observer_);
+ operator_info_->AddObserver(&second_observer_);
+
+ EXPECT_CALL(observer_, OnOperatorChanged()).Times(2);
+ EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyMVNOWithUUID("uuid200101");
+
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_P(MobileOperatorInfoObserverTest, LateObserver) {
+ // - Add one observer, and verify it gets an MVNO update.
+ operator_info_->AddObserver(&observer_);
+
+ EXPECT_CALL(observer_, OnOperatorChanged()).Times(2);
+ EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(0);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyMVNOWithUUID("uuid200101");
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(&observer_);
+ Mock::VerifyAndClearExpectations(&second_observer_);
+
+ EXPECT_CALL(observer_, OnOperatorChanged()).Times(1);
+ EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(0);
+ operator_info_->Reset();
+ VerifyNoMatch();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(&observer_);
+ Mock::VerifyAndClearExpectations(&second_observer_);
+
+ // - Add another observer, verify both get an MVNO update.
+ operator_info_->AddObserver(&second_observer_);
+
+ EXPECT_CALL(observer_, OnOperatorChanged()).Times(2);
+ EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyMVNOWithUUID("uuid200101");
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(&observer_);
+ Mock::VerifyAndClearExpectations(&second_observer_);
+
+ EXPECT_CALL(observer_, OnOperatorChanged()).Times(1);
+ EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(1);
+ operator_info_->Reset();
+ VerifyNoMatch();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(&observer_);
+ Mock::VerifyAndClearExpectations(&second_observer_);
+
+ // - Remove an observer, verify it no longer gets updates.
+ operator_info_->RemoveObserver(&observer_);
+
+ EXPECT_CALL(observer_, OnOperatorChanged()).Times(0);
+ EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(2);
+ UpdateMCCMNC("200001");
+ UpdateOperatorName("name200101");
+ VerifyMVNOWithUUID("uuid200101");
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(&observer_);
+ Mock::VerifyAndClearExpectations(&second_observer_);
+
+ EXPECT_CALL(observer_, OnOperatorChanged()).Times(0);
+ EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(1);
+ operator_info_->Reset();
+ VerifyNoMatch();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(&observer_);
+ Mock::VerifyAndClearExpectations(&second_observer_);
+}
+
+INSTANTIATE_TEST_CASE_P(MobileOperatorInfoMainTestInstance,
+ MobileOperatorInfoMainTest,
+ Values(kEventCheckingPolicyStrict,
+ kEventCheckingPolicyNonStrict));
+INSTANTIATE_TEST_CASE_P(MobileOperatorInfoDataTestInstance,
+ MobileOperatorInfoDataTest,
+ Values(kEventCheckingPolicyStrict,
+ kEventCheckingPolicyNonStrict));
+// It only makes sense to do strict checking here.
+INSTANTIATE_TEST_CASE_P(MobileOperatorInfoObserverTestInstance,
+ MobileOperatorInfoObserverTest,
+ Values(kEventCheckingPolicyStrict));
+} // namespace shill
diff --git a/cellular/mock_cellular.cc b/cellular/mock_cellular.cc
new file mode 100644
index 0000000..509e7d4
--- /dev/null
+++ b/cellular/mock_cellular.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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/mock_cellular.h"
+
+#include <gmock/gmock.h>
+
+#include "shill/error.h"
+
+namespace shill {
+
+// TODO(rochberg): The cellular constructor does work. Ought to fix
+// this so that we don't depend on passing real values in for Type.
+
+MockCellular::MockCellular(ModemInfo *modem_info,
+ const std::string &link_name,
+ const std::string &address,
+ int interface_index,
+ Type type,
+ const std::string &owner,
+ const std::string &service,
+ const std::string &path,
+ ProxyFactory *proxy_factory)
+ : Cellular(modem_info, link_name, address, interface_index, type, owner,
+ service, path, proxy_factory) {}
+
+MockCellular::~MockCellular() {}
+
+} // namespace shill
diff --git a/cellular/mock_cellular.h b/cellular/mock_cellular.h
new file mode 100644
index 0000000..7d57148
--- /dev/null
+++ b/cellular/mock_cellular.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_CELLULAR_H_
+#define SHILL_CELLULAR_MOCK_CELLULAR_H_
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "shill/cellular/cellular.h"
+
+namespace shill {
+
+class MockCellular : public Cellular {
+ public:
+ MockCellular(ModemInfo *modem_info,
+ const std::string &link_name,
+ const std::string &address,
+ int interface_index,
+ Type type,
+ const std::string &owner,
+ const std::string &service,
+ const std::string &path,
+ ProxyFactory *proxy_factory);
+ ~MockCellular() override;
+
+ MOCK_METHOD1(Connect, void(Error *error));
+ MOCK_METHOD2(Disconnect, void(Error *error, const char *reason));
+ MOCK_METHOD3(OnDBusPropertiesChanged, void(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties));
+ MOCK_METHOD1(set_modem_state, void(ModemState state));
+ MOCK_METHOD0(DestroyService, void());
+ MOCK_METHOD1(StartPPP, void(const std::string &serial_device));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockCellular);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_CELLULAR_H_
diff --git a/cellular/mock_cellular_service.cc b/cellular/mock_cellular_service.cc
new file mode 100644
index 0000000..64e7d2b
--- /dev/null
+++ b/cellular/mock_cellular_service.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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/mock_cellular_service.h"
+
+#include <chromeos/dbus/service_constants.h>
+
+using testing::ReturnRef;
+
+namespace shill {
+
+MockCellularService::MockCellularService(ModemInfo *modem_info,
+ const CellularRefPtr &device)
+ : CellularService(modem_info, device),
+ default_activation_state_(kActivationStateUnknown) {
+ ON_CALL(*this, activation_state())
+ .WillByDefault(ReturnRef(default_activation_state_));
+}
+
+MockCellularService::~MockCellularService() {}
+
+} // namespace shill
diff --git a/cellular/mock_cellular_service.h b/cellular/mock_cellular_service.h
new file mode 100644
index 0000000..f78d852
--- /dev/null
+++ b/cellular/mock_cellular_service.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_CELLULAR_SERVICE_H_
+#define SHILL_CELLULAR_MOCK_CELLULAR_SERVICE_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "shill/cellular/cellular_service.h"
+
+namespace shill {
+
+class MockCellularService : public CellularService {
+ public:
+ MockCellularService(ModemInfo *modem_info,
+ const CellularRefPtr &device);
+ ~MockCellularService() override;
+
+ MOCK_METHOD0(AutoConnect, void());
+ MOCK_METHOD1(SetLastGoodApn, void(const Stringmap &apn_info));
+ MOCK_METHOD0(ClearLastGoodApn, void());
+ MOCK_METHOD1(SetActivationState, void(const std::string &state));
+ MOCK_METHOD2(Connect, void(Error *error, const char *reason));
+ MOCK_METHOD2(Disconnect, void(Error *error, const char *reason));
+ MOCK_METHOD1(SetState, void(ConnectState state));
+ MOCK_METHOD1(SetFailure, void(ConnectFailure failure));
+ MOCK_METHOD1(SetFailureSilent, void(ConnectFailure failure));
+ MOCK_CONST_METHOD0(state, ConnectState());
+ MOCK_CONST_METHOD0(explicitly_disconnected, bool());
+ MOCK_CONST_METHOD0(activation_state, const std::string &());
+ MOCK_CONST_METHOD0(resume_start_time, const base::Time &());
+
+ private:
+ std::string default_activation_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCellularService);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_CELLULAR_SERVICE_H_
diff --git a/cellular/mock_dbus_objectmanager_proxy.cc b/cellular/mock_dbus_objectmanager_proxy.cc
new file mode 100644
index 0000000..b6d9c34
--- /dev/null
+++ b/cellular/mock_dbus_objectmanager_proxy.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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/mock_dbus_objectmanager_proxy.h"
+
+#include "shill/testing.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+
+namespace shill {
+MockDBusObjectManagerProxy::MockDBusObjectManagerProxy() {
+ ON_CALL(*this, GetManagedObjects(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockDBusObjectManagerProxy::~MockDBusObjectManagerProxy() {}
+
+void MockDBusObjectManagerProxy::IgnoreSetCallbacks() {
+ EXPECT_CALL(*this, set_interfaces_added_callback(_)).Times(AnyNumber());
+ EXPECT_CALL(*this, set_interfaces_removed_callback(_)).Times(AnyNumber());
+}
+} // namespace shill
diff --git a/cellular/mock_dbus_objectmanager_proxy.h b/cellular/mock_dbus_objectmanager_proxy.h
new file mode 100644
index 0000000..0e50c1b
--- /dev/null
+++ b/cellular/mock_dbus_objectmanager_proxy.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_DBUS_OBJECTMANAGER_PROXY_H_
+#define SHILL_CELLULAR_MOCK_DBUS_OBJECTMANAGER_PROXY_H_
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/dbus_objectmanager_proxy_interface.h"
+
+namespace shill {
+
+class MockDBusObjectManagerProxy : public DBusObjectManagerProxyInterface {
+ public:
+ MockDBusObjectManagerProxy();
+ ~MockDBusObjectManagerProxy() override;
+
+ MOCK_METHOD3(GetManagedObjects, void(Error *error,
+ const ManagedObjectsCallback &callback,
+ int timeout));
+ MOCK_METHOD1(set_interfaces_added_callback,
+ void(const InterfacesAddedSignalCallback &callback));
+ MOCK_METHOD1(set_interfaces_removed_callback,
+ void(const InterfacesRemovedSignalCallback &callback));
+ void IgnoreSetCallbacks();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockDBusObjectManagerProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_DBUS_OBJECTMANAGER_PROXY_H_
diff --git a/cellular/mock_mm1_bearer_proxy.cc b/cellular/mock_mm1_bearer_proxy.cc
new file mode 100644
index 0000000..f26be03
--- /dev/null
+++ b/cellular/mock_mm1_bearer_proxy.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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/mock_mm1_bearer_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+namespace mm1 {
+
+MockBearerProxy::MockBearerProxy() {
+ ON_CALL(*this, Connect(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, Disconnect(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockBearerProxy::~MockBearerProxy() {}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mock_mm1_bearer_proxy.h b/cellular/mock_mm1_bearer_proxy.h
new file mode 100644
index 0000000..e478967
--- /dev/null
+++ b/cellular/mock_mm1_bearer_proxy.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MM1_BEARER_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MM1_BEARER_PROXY_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mm1_bearer_proxy_interface.h"
+
+namespace shill {
+namespace mm1 {
+
+class MockBearerProxy : public BearerProxyInterface {
+ public:
+ MockBearerProxy();
+ ~MockBearerProxy() override;
+
+ MOCK_METHOD3(Connect, void(Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD3(Disconnect, void(Error *error,
+ const ResultCallback &callback,
+ int timeout));
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MM1_BEARER_PROXY_H_
diff --git a/cellular/mock_mm1_modem_location_proxy.cc b/cellular/mock_mm1_modem_location_proxy.cc
new file mode 100644
index 0000000..a62376f
--- /dev/null
+++ b/cellular/mock_mm1_modem_location_proxy.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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/mock_mm1_modem_location_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+namespace mm1 {
+
+MockModemLocationProxy::MockModemLocationProxy() {
+ ON_CALL(*this, Setup(_, _, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<2>());
+ ON_CALL(*this, GetLocation(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockModemLocationProxy::~MockModemLocationProxy() {}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mock_mm1_modem_location_proxy.h b/cellular/mock_mm1_modem_location_proxy.h
new file mode 100644
index 0000000..3324807
--- /dev/null
+++ b/cellular/mock_mm1_modem_location_proxy.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MM1_MODEM_LOCATION_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MM1_MODEM_LOCATION_PROXY_H_
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mm1_modem_location_proxy_interface.h"
+
+namespace shill {
+namespace mm1 {
+
+class MockModemLocationProxy : public ModemLocationProxyInterface {
+ public:
+ MockModemLocationProxy();
+ ~MockModemLocationProxy() override;
+
+ // Inherited methods from ModemLocationProxyInterface.
+ MOCK_METHOD5(Setup, void(uint32_t sources,
+ bool signal_location,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+
+ MOCK_METHOD3(GetLocation, void(Error *error,
+ const DBusEnumValueMapCallback &callback,
+ int timeout));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemLocationProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MM1_MODEM_LOCATION_PROXY_H_
diff --git a/cellular/mock_mm1_modem_modem3gpp_proxy.cc b/cellular/mock_mm1_modem_modem3gpp_proxy.cc
new file mode 100644
index 0000000..ad985dc
--- /dev/null
+++ b/cellular/mock_mm1_modem_modem3gpp_proxy.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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/mock_mm1_modem_modem3gpp_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+namespace mm1 {
+
+MockModemModem3gppProxy::MockModemModem3gppProxy() {
+ ON_CALL(*this, Register(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, Scan(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockModemModem3gppProxy::~MockModemModem3gppProxy() {}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mock_mm1_modem_modem3gpp_proxy.h b/cellular/mock_mm1_modem_modem3gpp_proxy.h
new file mode 100644
index 0000000..7e9bba4
--- /dev/null
+++ b/cellular/mock_mm1_modem_modem3gpp_proxy.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MM1_MODEM_MODEM3GPP_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MM1_MODEM_MODEM3GPP_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mm1_modem_modem3gpp_proxy_interface.h"
+
+namespace shill {
+namespace mm1 {
+
+class MockModemModem3gppProxy : public ModemModem3gppProxyInterface {
+ public:
+ MockModemModem3gppProxy();
+ ~MockModemModem3gppProxy() override;
+
+ MOCK_METHOD4(Register, void(const std::string &operator_id,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD3(Scan, void(Error *error,
+ const DBusPropertyMapsCallback &callback,
+ int timeout));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemModem3gppProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MM1_MODEM_MODEM3GPP_PROXY_H_
diff --git a/cellular/mock_mm1_modem_modemcdma_proxy.cc b/cellular/mock_mm1_modem_modemcdma_proxy.cc
new file mode 100644
index 0000000..650afd4
--- /dev/null
+++ b/cellular/mock_mm1_modem_modemcdma_proxy.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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/mock_mm1_modem_modemcdma_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+namespace mm1 {
+
+MockModemModemCdmaProxy::MockModemModemCdmaProxy() {
+ ON_CALL(*this, Activate(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, ActivateManual(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+}
+
+MockModemModemCdmaProxy::~MockModemModemCdmaProxy() {}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mock_mm1_modem_modemcdma_proxy.h b/cellular/mock_mm1_modem_modemcdma_proxy.h
new file mode 100644
index 0000000..39c2f83
--- /dev/null
+++ b/cellular/mock_mm1_modem_modemcdma_proxy.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MM1_MODEM_MODEMCDMA_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MM1_MODEM_MODEMCDMA_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mm1_modem_modemcdma_proxy_interface.h"
+
+namespace shill {
+namespace mm1 {
+
+class MockModemModemCdmaProxy : public ModemModemCdmaProxyInterface {
+ public:
+ MockModemModemCdmaProxy();
+ ~MockModemModemCdmaProxy() override;
+
+ MOCK_METHOD4(Activate, void(
+ const std::string &carrier,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+
+ MOCK_METHOD4(ActivateManual, void(
+ const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+
+ MOCK_METHOD1(set_activation_state_callback,
+ void(const ActivationStateSignalCallback &callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemModemCdmaProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MM1_MODEM_MODEMCDMA_PROXY_H_
diff --git a/cellular/mock_mm1_modem_proxy.cc b/cellular/mock_mm1_modem_proxy.cc
new file mode 100644
index 0000000..e223fbe
--- /dev/null
+++ b/cellular/mock_mm1_modem_proxy.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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/mock_mm1_modem_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+namespace mm1 {
+
+MockModemProxy::MockModemProxy() {
+ ON_CALL(*this, Enable(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, CreateBearer(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, DeleteBearer(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, Reset(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, FactoryReset(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, SetCurrentCapabilities(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, SetCurrentModes(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, Command(_, _, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<2>());
+ ON_CALL(*this, SetPowerState(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+}
+
+MockModemProxy::~MockModemProxy() {}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mock_mm1_modem_proxy.h b/cellular/mock_mm1_modem_proxy.h
new file mode 100644
index 0000000..3fce369
--- /dev/null
+++ b/cellular/mock_mm1_modem_proxy.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MM1_MODEM_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MM1_MODEM_PROXY_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mm1_modem_proxy_interface.h"
+
+namespace shill {
+namespace mm1 {
+
+class MockModemProxy : public ModemProxyInterface {
+ public:
+ MockModemProxy();
+ ~MockModemProxy() override;
+
+ // Inherited methods from ModemProxyInterface.
+ MOCK_METHOD4(Enable, void(bool enable,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD4(CreateBearer, void(const DBusPropertiesMap &properties,
+ Error *error,
+ const DBusPathCallback &callback,
+ int timeout));
+ MOCK_METHOD4(DeleteBearer, void(const ::DBus::Path &bearer,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD3(Reset, void(Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD4(FactoryReset, void(const std::string &code,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD4(SetCurrentCapabilities, void(const uint32_t &capabilities,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD4(SetCurrentModes,
+ void(const ::DBus::Struct<uint32_t, uint32_t> &modes,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD4(SetCurrentBands, void(const std::vector<uint32_t> &bands,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD5(Command, void(const std::string &cmd,
+ const uint32_t &user_timeout,
+ Error *error,
+ const StringCallback &callback,
+ int timeout));
+ MOCK_METHOD4(SetPowerState, void(const uint32_t &power_state,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD1(set_state_changed_callback, void(
+ const ModemStateChangedSignalCallback &callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MM1_MODEM_PROXY_H_
diff --git a/cellular/mock_mm1_modem_simple_proxy.cc b/cellular/mock_mm1_modem_simple_proxy.cc
new file mode 100644
index 0000000..d985d3a
--- /dev/null
+++ b/cellular/mock_mm1_modem_simple_proxy.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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/mock_mm1_modem_simple_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+namespace mm1 {
+
+MockModemSimpleProxy::MockModemSimpleProxy() {
+ ON_CALL(*this, Connect(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, Disconnect(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, GetStatus(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockModemSimpleProxy::~MockModemSimpleProxy() {}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mock_mm1_modem_simple_proxy.h b/cellular/mock_mm1_modem_simple_proxy.h
new file mode 100644
index 0000000..e0f1863
--- /dev/null
+++ b/cellular/mock_mm1_modem_simple_proxy.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MM1_MODEM_SIMPLE_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MM1_MODEM_SIMPLE_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mm1_modem_simple_proxy_interface.h"
+
+namespace shill {
+namespace mm1 {
+
+class MockModemSimpleProxy : public ModemSimpleProxyInterface {
+ public:
+ MockModemSimpleProxy();
+ ~MockModemSimpleProxy() override;
+
+ MOCK_METHOD4(Connect, void(const DBusPropertiesMap &properties,
+ Error *error,
+ const DBusPathCallback &callback,
+ int timeout));
+ MOCK_METHOD4(Disconnect, void(const ::DBus::Path &bearer,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD3(GetStatus, void(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemSimpleProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MM1_MODEM_SIMPLE_PROXY_H_
diff --git a/cellular/mock_mm1_modem_time_proxy.cc b/cellular/mock_mm1_modem_time_proxy.cc
new file mode 100644
index 0000000..1e0f2aa
--- /dev/null
+++ b/cellular/mock_mm1_modem_time_proxy.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2012 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/mock_mm1_modem_time_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+namespace mm1 {
+
+MockModemTimeProxy::MockModemTimeProxy() {
+ ON_CALL(*this, GetNetworkTime(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockModemTimeProxy::~MockModemTimeProxy() {}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mock_mm1_modem_time_proxy.h b/cellular/mock_mm1_modem_time_proxy.h
new file mode 100644
index 0000000..2dd1e09
--- /dev/null
+++ b/cellular/mock_mm1_modem_time_proxy.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MM1_MODEM_TIME_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MM1_MODEM_TIME_PROXY_H_
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mm1_modem_time_proxy_interface.h"
+
+namespace shill {
+namespace mm1 {
+
+class MockModemTimeProxy : public ModemTimeProxyInterface {
+ public:
+ MockModemTimeProxy();
+ ~MockModemTimeProxy() override;
+
+ // Inherited methods from ModemTimeProxyInterface.
+ MOCK_METHOD3(GetNetworkTime, void(Error *error,
+ const StringCallback &callback,
+ int timeout));
+
+ MOCK_METHOD1(set_network_time_changed_callback,
+ void(const NetworkTimeChangedSignalCallback &callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemTimeProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MM1_MODEM_TIME_PROXY_H_
diff --git a/cellular/mock_mm1_sim_proxy.cc b/cellular/mock_mm1_sim_proxy.cc
new file mode 100644
index 0000000..760e7a0
--- /dev/null
+++ b/cellular/mock_mm1_sim_proxy.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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/mock_mm1_sim_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+namespace mm1 {
+
+MockSimProxy::MockSimProxy() {
+ ON_CALL(*this, SendPin(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, SendPuk(_, _, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<2>());
+ ON_CALL(*this, EnablePin(_, _, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<2>());
+ ON_CALL(*this, ChangePin(_, _, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<2>());
+}
+
+MockSimProxy::~MockSimProxy() {}
+
+} // namespace mm1
+} // namespace shill
diff --git a/cellular/mock_mm1_sim_proxy.h b/cellular/mock_mm1_sim_proxy.h
new file mode 100644
index 0000000..5282439
--- /dev/null
+++ b/cellular/mock_mm1_sim_proxy.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MM1_SIM_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MM1_SIM_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mm1_sim_proxy_interface.h"
+
+namespace shill {
+namespace mm1 {
+
+class MockSimProxy : public SimProxyInterface {
+ public:
+ MockSimProxy();
+ ~MockSimProxy() override;
+
+ MOCK_METHOD4(SendPin, void(const std::string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD5(SendPuk, void(const std::string &puk,
+ const std::string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD5(EnablePin, void(const std::string &pin,
+ const bool enabled,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD5(ChangePin, void(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSimProxy);
+};
+
+} // namespace mm1
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MM1_SIM_PROXY_H_
diff --git a/cellular/mock_mobile_operator_info.cc b/cellular/mock_mobile_operator_info.cc
new file mode 100644
index 0000000..2027a87
--- /dev/null
+++ b/cellular/mock_mobile_operator_info.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2014 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/mock_mobile_operator_info.h"
+
+#include <gmock/gmock.h>
+
+namespace shill {
+
+MockMobileOperatorInfo::MockMobileOperatorInfo(EventDispatcher *dispatcher,
+ const std::string &info_owner)
+ : MobileOperatorInfo(dispatcher, info_owner) {}
+
+MockMobileOperatorInfo::~MockMobileOperatorInfo() {}
+
+void MockMobileOperatorInfo::SetEmptyDefaultsForProperties() {
+ ON_CALL(*this, mccmnc()).WillByDefault(ReturnRef(empty_mccmnc_));
+ ON_CALL(*this, olp_list()).WillByDefault(ReturnRef(empty_olp_list_));
+ ON_CALL(*this, activation_code())
+ .WillByDefault(ReturnRef(empty_activation_code_));
+ ON_CALL(*this, operator_name())
+ .WillByDefault(ReturnRef(empty_operator_name_));
+ ON_CALL(*this, country())
+ .WillByDefault(ReturnRef(empty_country_));
+ ON_CALL(*this, uuid()).WillByDefault(ReturnRef(empty_uuid_));
+}
+
+} // namespace shill
diff --git a/cellular/mock_mobile_operator_info.h b/cellular/mock_mobile_operator_info.h
new file mode 100644
index 0000000..6c65c9c
--- /dev/null
+++ b/cellular/mock_mobile_operator_info.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2014 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MOBILE_OPERATOR_INFO_H_
+#define SHILL_CELLULAR_MOCK_MOBILE_OPERATOR_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "shill/cellular/mobile_operator_info.h"
+
+using testing::ReturnRef;
+
+namespace shill {
+
+class MockMobileOperatorInfo : public MobileOperatorInfo {
+ public:
+ MockMobileOperatorInfo(EventDispatcher *dispatcher,
+ const std::string &info_owner);
+ ~MockMobileOperatorInfo() override;
+
+ MOCK_CONST_METHOD0(IsMobileNetworkOperatorKnown, bool());
+
+ MOCK_CONST_METHOD0(mccmnc, const std::string &());
+ MOCK_CONST_METHOD0(olp_list,
+ const std::vector<MobileOperatorInfo::OnlinePortal> &());
+ MOCK_CONST_METHOD0(activation_code, const std::string &());
+ MOCK_CONST_METHOD0(operator_name, const std::string &());
+ MOCK_CONST_METHOD0(country, const std::string &());
+ MOCK_CONST_METHOD0(uuid, const std::string &());
+
+ MOCK_METHOD1(UpdateMCCMNC, void(const std::string &));
+ MOCK_METHOD1(UpdateSID, void(const std::string &));
+ MOCK_METHOD1(UpdateIMSI, void(const std::string &));
+ MOCK_METHOD1(UpdateNID, void(const std::string &));
+ MOCK_METHOD1(UpdateOperatorName, void(const std::string &));
+
+ // Sets up the mock object to return empty strings/vectors etc for all
+ // propeties.
+ void SetEmptyDefaultsForProperties();
+
+ private:
+ std::string empty_mccmnc_;
+ std::vector<MobileOperatorInfo::OnlinePortal> empty_olp_list_;
+ std::string empty_activation_code_;
+ std::string empty_operator_name_;
+ std::string empty_country_;
+ std::string empty_uuid_;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MOBILE_OPERATOR_INFO_H_
diff --git a/cellular/mock_modem.cc b/cellular/mock_modem.cc
new file mode 100644
index 0000000..782c6d5
--- /dev/null
+++ b/cellular/mock_modem.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 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/mock_modem.h"
+
+namespace shill {
+
+MockModem::MockModem(const std::string &owner,
+ const std::string &service,
+ const std::string &path,
+ ModemInfo *modem_info)
+ : Modem(owner, service, path, modem_info) {}
+
+MockModem::~MockModem() {}
+
+} // namespace shill
diff --git a/cellular/mock_modem.h b/cellular/mock_modem.h
new file mode 100644
index 0000000..01b7f6c
--- /dev/null
+++ b/cellular/mock_modem.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_H_
+#define SHILL_CELLULAR_MOCK_MODEM_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem.h"
+
+namespace shill {
+
+class MockModem : public Modem {
+ public:
+ MockModem(const std::string &owner,
+ const std::string &service,
+ const std::string &path,
+ ModemInfo *modem_info);
+ ~MockModem() override;
+
+ // This class only mocks the pure virtual methods; if you need a
+ // more thorough mock, know that modem_unittest.cc depends on the
+ // incompleteness of this mock.
+ MOCK_METHOD1(SetModemStateFromProperties,
+ void(const DBusPropertiesMap &properties));
+ MOCK_CONST_METHOD2(GetLinkName,
+ bool(const DBusPropertiesMap &modem_properties,
+ std::string *name));
+ MOCK_CONST_METHOD0(GetModemInterface,
+ std::string(void));
+ MOCK_METHOD3(ConstructCellular, Cellular *(
+ const std::string &link_name,
+ const std::string &device_name,
+ int ifindex));
+};
+typedef ::testing::StrictMock<MockModem> StrictModem;
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_H_
diff --git a/cellular/mock_modem_cdma_proxy.cc b/cellular/mock_modem_cdma_proxy.cc
new file mode 100644
index 0000000..bceacae
--- /dev/null
+++ b/cellular/mock_modem_cdma_proxy.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 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/mock_modem_cdma_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+
+MockModemCDMAProxy::MockModemCDMAProxy() {
+ ON_CALL(*this, Activate(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, GetRegistrationState(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, GetSignalQuality(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockModemCDMAProxy::~MockModemCDMAProxy() {}
+
+} // namespace shill
diff --git a/cellular/mock_modem_cdma_proxy.h b/cellular/mock_modem_cdma_proxy.h
new file mode 100644
index 0000000..0086691
--- /dev/null
+++ b/cellular/mock_modem_cdma_proxy.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_CDMA_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MODEM_CDMA_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem_cdma_proxy_interface.h"
+
+namespace shill {
+
+class MockModemCDMAProxy : public ModemCDMAProxyInterface {
+ public:
+ MockModemCDMAProxy();
+ ~MockModemCDMAProxy() override;
+
+ MOCK_METHOD4(Activate, void(const std::string &carrier, Error *error,
+ const ActivationResultCallback &callback,
+ int timeout));
+ MOCK_METHOD3(GetRegistrationState,
+ void(Error *error, const RegistrationStateCallback &callback,
+ int timeout));
+ MOCK_METHOD3(GetSignalQuality, void(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout));
+ MOCK_METHOD0(MEID, const std::string());
+ MOCK_METHOD1(set_activation_state_callback,
+ void(const ActivationStateSignalCallback &callback));
+ MOCK_METHOD1(set_signal_quality_callback,
+ void(const SignalQualitySignalCallback &callback));
+ MOCK_METHOD1(set_registration_state_callback,
+ void(const RegistrationStateSignalCallback &callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemCDMAProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_CDMA_PROXY_H_
diff --git a/cellular/mock_modem_gobi_proxy.cc b/cellular/mock_modem_gobi_proxy.cc
new file mode 100644
index 0000000..d99e750
--- /dev/null
+++ b/cellular/mock_modem_gobi_proxy.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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/mock_modem_gobi_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+
+MockModemGobiProxy::MockModemGobiProxy() {
+ ON_CALL(*this, SetCarrier(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+}
+
+MockModemGobiProxy::~MockModemGobiProxy() {}
+
+} // namespace shill
diff --git a/cellular/mock_modem_gobi_proxy.h b/cellular/mock_modem_gobi_proxy.h
new file mode 100644
index 0000000..f05c867
--- /dev/null
+++ b/cellular/mock_modem_gobi_proxy.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_GOBI_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MODEM_GOBI_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem_gobi_proxy_interface.h"
+
+namespace shill {
+
+class MockModemGobiProxy : public ModemGobiProxyInterface {
+ public:
+ MockModemGobiProxy();
+ ~MockModemGobiProxy() override;
+
+ MOCK_METHOD4(SetCarrier, void(const std::string &carrier,
+ Error *error, const ResultCallback &callback,
+ int timeout));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemGobiProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_GOBI_PROXY_H_
diff --git a/cellular/mock_modem_gsm_card_proxy.cc b/cellular/mock_modem_gsm_card_proxy.cc
new file mode 100644
index 0000000..b044628
--- /dev/null
+++ b/cellular/mock_modem_gsm_card_proxy.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 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/mock_modem_gsm_card_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+
+MockModemGSMCardProxy::MockModemGSMCardProxy() {
+ ON_CALL(*this, GetIMEI(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, GetIMSI(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, GetSPN(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, GetMSISDN(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, EnablePIN(_, _, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<2>());
+ ON_CALL(*this, SendPIN(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, SendPUK(_, _, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<2>());
+ ON_CALL(*this, ChangePIN(_, _, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<2>());
+}
+
+MockModemGSMCardProxy::~MockModemGSMCardProxy() {}
+
+} // namespace shill
diff --git a/cellular/mock_modem_gsm_card_proxy.h b/cellular/mock_modem_gsm_card_proxy.h
new file mode 100644
index 0000000..6ea2b9f
--- /dev/null
+++ b/cellular/mock_modem_gsm_card_proxy.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_GSM_CARD_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MODEM_GSM_CARD_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem_gsm_card_proxy_interface.h"
+
+namespace shill {
+
+class MockModemGSMCardProxy : public ModemGSMCardProxyInterface {
+ public:
+ MockModemGSMCardProxy();
+ ~MockModemGSMCardProxy() override;
+
+ MOCK_METHOD3(GetIMEI, void(Error *error,
+ const GSMIdentifierCallback &callback,
+ int timeout));
+ MOCK_METHOD3(GetIMSI, void(Error *error,
+ const GSMIdentifierCallback &callback,
+ int timeout));
+ MOCK_METHOD3(GetSPN, void(Error *error,
+ const GSMIdentifierCallback &callback,
+ int timeout));
+ MOCK_METHOD3(GetMSISDN, void(Error *error,
+ const GSMIdentifierCallback &callback,
+ int timeout));
+
+ MOCK_METHOD5(EnablePIN, void(const std::string &pin, bool enabled,
+ Error *error, const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD4(SendPIN, void(const std::string &pin,
+ Error *error, const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD5(SendPUK, void(const std::string &puk, const std::string &pin,
+ Error *error, const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD5(ChangePIN, void(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error, const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD0(EnabledFacilityLocks, uint32_t());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemGSMCardProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_GSM_CARD_PROXY_H_
diff --git a/cellular/mock_modem_gsm_network_proxy.cc b/cellular/mock_modem_gsm_network_proxy.cc
new file mode 100644
index 0000000..8a1889a
--- /dev/null
+++ b/cellular/mock_modem_gsm_network_proxy.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 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/mock_modem_gsm_network_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+
+MockModemGSMNetworkProxy::MockModemGSMNetworkProxy() {
+ ON_CALL(*this, GetRegistrationInfo(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, GetSignalQuality(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, Register(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, Scan(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockModemGSMNetworkProxy::~MockModemGSMNetworkProxy() {}
+
+} // namespace shill
diff --git a/cellular/mock_modem_gsm_network_proxy.h b/cellular/mock_modem_gsm_network_proxy.h
new file mode 100644
index 0000000..070f63d
--- /dev/null
+++ b/cellular/mock_modem_gsm_network_proxy.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_GSM_NETWORK_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MODEM_GSM_NETWORK_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem_gsm_network_proxy_interface.h"
+
+namespace shill {
+
+class MockModemGSMNetworkProxy : public ModemGSMNetworkProxyInterface {
+ public:
+ MockModemGSMNetworkProxy();
+ ~MockModemGSMNetworkProxy() override;
+
+ MOCK_METHOD3(GetRegistrationInfo,
+ void(Error *error, const RegistrationInfoCallback &callback,
+ int timeout));
+ MOCK_METHOD3(GetSignalQuality, void(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout));
+ MOCK_METHOD4(Register, void(const std::string &network_id, Error *error,
+ const ResultCallback &callback, int timeout));
+ MOCK_METHOD3(Scan, void(Error *error, const ScanResultsCallback &callback,
+ int timeout));
+ MOCK_METHOD0(AccessTechnology, uint32_t());
+ MOCK_METHOD1(set_signal_quality_callback,
+ void(const SignalQualitySignalCallback &callback));
+ MOCK_METHOD1(set_network_mode_callback,
+ void(const NetworkModeSignalCallback &callback));
+ MOCK_METHOD1(set_registration_info_callback,
+ void(const RegistrationInfoSignalCallback &callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemGSMNetworkProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_GSM_NETWORK_PROXY_H_
diff --git a/cellular/mock_modem_info.cc b/cellular/mock_modem_info.cc
new file mode 100644
index 0000000..262dd0e
--- /dev/null
+++ b/cellular/mock_modem_info.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 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/mock_modem_info.h"
+
+namespace shill {
+
+MockModemInfo::MockModemInfo() :
+ ModemInfo(nullptr, nullptr, nullptr, nullptr, nullptr),
+ mock_pending_activation_store_(nullptr) {}
+
+MockModemInfo::MockModemInfo(ControlInterface *control,
+ EventDispatcher *dispatcher,
+ Metrics *metrics,
+ Manager *manager,
+ GLib *glib) :
+ ModemInfo(control, dispatcher, metrics, manager, glib),
+ mock_pending_activation_store_(nullptr) {
+ SetMockMembers();
+}
+
+MockModemInfo::~MockModemInfo() {}
+
+void MockModemInfo::SetMockMembers() {
+ // These are always replaced by mocks.
+ // Assumes ownership.
+ set_pending_activation_store(new MockPendingActivationStore());
+ mock_pending_activation_store_ =
+ static_cast<MockPendingActivationStore*>(pending_activation_store());
+ // These are replaced by mocks only if current unset in ModemInfo.
+ if (control_interface() == nullptr) {
+ mock_control_.reset(new MockControl());
+ set_control_interface(mock_control_.get());
+ }
+ if (dispatcher() == nullptr) {
+ mock_dispatcher_.reset(new MockEventDispatcher());
+ set_event_dispatcher(mock_dispatcher_.get());
+ }
+ if (metrics() == nullptr) {
+ mock_metrics_.reset(new MockMetrics(dispatcher()));
+ set_metrics(mock_metrics_.get());
+ }
+ if (glib() == nullptr) {
+ mock_glib_.reset(new MockGLib());
+ set_glib(mock_glib_.get());
+ }
+ if (manager() == nullptr) {
+ mock_manager_.reset(new MockManager(control_interface(), dispatcher(),
+ metrics(), glib()));
+ set_manager(mock_manager_.get());
+ }
+}
+
+} // namespace shill
diff --git a/cellular/mock_modem_info.h b/cellular/mock_modem_info.h
new file mode 100644
index 0000000..093ed76
--- /dev/null
+++ b/cellular/mock_modem_info.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_INFO_H_
+#define SHILL_CELLULAR_MOCK_MODEM_INFO_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem_info.h"
+#include "shill/mock_control.h"
+#include "shill/mock_event_dispatcher.h"
+#include "shill/mock_glib.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_metrics.h"
+#include "shill/mock_pending_activation_store.h"
+
+namespace shill {
+
+class MockModemInfo : public ModemInfo {
+ public:
+ MockModemInfo();
+
+ // All nullptr parameters are replaced by mock objects.
+ MockModemInfo(ControlInterface *control,
+ EventDispatcher *dispatcher,
+ Metrics *metrics,
+ Manager *manager,
+ GLib *glib);
+
+ ~MockModemInfo() override;
+
+ // Replaces data members in ModemInfo by mock objects.
+ // The following are relaced by mocks if they are nullptr: control_interface,
+ // dispatcher, metrics, manager, glib.
+ // The following are always replaced by mocks: pending_activation_store.
+ void SetMockMembers();
+
+ // Accessors for mock objects
+ MockPendingActivationStore* mock_pending_activation_store() const {
+ return mock_pending_activation_store_;
+ }
+ MockControl* mock_control_interface() const {
+ return mock_control_.get();
+ }
+ MockEventDispatcher* mock_dispatcher() const {
+ return mock_dispatcher_.get();
+ }
+ MockMetrics* mock_metrics() const {
+ return mock_metrics_.get();
+ }
+ MockManager* mock_manager() const {
+ return mock_manager_.get();
+ }
+ MockGLib* mock_glib() const {
+ return mock_glib_.get();
+ }
+
+ MOCK_METHOD0(Start, void());
+ MOCK_METHOD0(Stop, void());
+ MOCK_METHOD1(OnDeviceInfoAvailable, void(const std::string &link_name));
+
+ private:
+ std::unique_ptr<MockControl> mock_control_;
+ std::unique_ptr<MockEventDispatcher> mock_dispatcher_;
+ std::unique_ptr<MockMetrics> mock_metrics_;
+ std::unique_ptr<MockManager> mock_manager_;
+ std::unique_ptr<MockGLib> mock_glib_;
+
+ // owned by ModemInfo
+ MockPendingActivationStore *mock_pending_activation_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockModemInfo);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_INFO_H_
diff --git a/cellular/mock_modem_manager_proxy.cc b/cellular/mock_modem_manager_proxy.cc
new file mode 100644
index 0000000..459fc86
--- /dev/null
+++ b/cellular/mock_modem_manager_proxy.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2011 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/mock_modem_manager_proxy.h"
+
+namespace shill {
+
+MockModemManagerProxy::MockModemManagerProxy() {}
+
+MockModemManagerProxy::~MockModemManagerProxy() {}
+
+} // namespace shill
diff --git a/cellular/mock_modem_manager_proxy.h b/cellular/mock_modem_manager_proxy.h
new file mode 100644
index 0000000..aca92db
--- /dev/null
+++ b/cellular/mock_modem_manager_proxy.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_MANAGER_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MODEM_MANAGER_PROXY_H_
+
+#include <vector>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem_manager_proxy_interface.h"
+
+namespace shill {
+
+class MockModemManagerProxy : public ModemManagerProxyInterface {
+ public:
+ MockModemManagerProxy();
+ ~MockModemManagerProxy() override;
+
+ MOCK_METHOD0(EnumerateDevices, std::vector<DBus::Path>());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemManagerProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_MANAGER_PROXY_H_
diff --git a/cellular/mock_modem_proxy.cc b/cellular/mock_modem_proxy.cc
new file mode 100644
index 0000000..47ae5f1
--- /dev/null
+++ b/cellular/mock_modem_proxy.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 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/mock_modem_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+
+MockModemProxy::MockModemProxy() {
+ ON_CALL(*this, Enable(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+ ON_CALL(*this, Disconnect(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, GetModemInfo(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+}
+
+MockModemProxy::~MockModemProxy() {}
+
+} // namespace shill
diff --git a/cellular/mock_modem_proxy.h b/cellular/mock_modem_proxy.h
new file mode 100644
index 0000000..ae1bade
--- /dev/null
+++ b/cellular/mock_modem_proxy.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MODEM_PROXY_H_
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem_proxy_interface.h"
+
+namespace shill {
+
+class MockModemProxy : public ModemProxyInterface {
+ public:
+ MockModemProxy();
+ ~MockModemProxy() override;
+
+ MOCK_METHOD4(Enable, void(bool enable, Error *error,
+ const ResultCallback &callback, int timeout));
+ MOCK_METHOD3(Disconnect, void(Error *error, const ResultCallback &callback,
+ int timeout));
+ MOCK_METHOD3(GetModemInfo, void(Error *error,
+ const ModemInfoCallback &callback,
+ int timeout));
+ MOCK_METHOD1(set_state_changed_callback,
+ void(const ModemStateChangedSignalCallback &callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_PROXY_H_
diff --git a/cellular/mock_modem_simple_proxy.cc b/cellular/mock_modem_simple_proxy.cc
new file mode 100644
index 0000000..9e9dc94
--- /dev/null
+++ b/cellular/mock_modem_simple_proxy.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 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/mock_modem_simple_proxy.h"
+
+#include "shill/testing.h"
+
+using testing::_;
+
+namespace shill {
+
+MockModemSimpleProxy::MockModemSimpleProxy() {
+ ON_CALL(*this, GetModemStatus(_, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<0>());
+ ON_CALL(*this, Connect(_, _, _, _))
+ .WillByDefault(SetOperationFailedInArgumentAndWarn<1>());
+}
+
+MockModemSimpleProxy::~MockModemSimpleProxy() {}
+
+} // namespace shill
diff --git a/cellular/mock_modem_simple_proxy.h b/cellular/mock_modem_simple_proxy.h
new file mode 100644
index 0000000..df7b951
--- /dev/null
+++ b/cellular/mock_modem_simple_proxy.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MOCK_MODEM_SIMPLE_PROXY_H_
+#define SHILL_CELLULAR_MOCK_MODEM_SIMPLE_PROXY_H_
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "shill/cellular/modem_simple_proxy_interface.h"
+
+namespace shill {
+
+class MockModemSimpleProxy : public ModemSimpleProxyInterface {
+ public:
+ MockModemSimpleProxy();
+ ~MockModemSimpleProxy() override;
+
+ MOCK_METHOD3(GetModemStatus, void(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout));
+ MOCK_METHOD4(Connect, void(const DBusPropertiesMap &properties,
+ Error *error, const ResultCallback &callback,
+ int timeout));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockModemSimpleProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_MODEM_SIMPLE_PROXY_H_
diff --git a/cellular/mock_out_of_credits_detector.cc b/cellular/mock_out_of_credits_detector.cc
new file mode 100644
index 0000000..76e9ccf
--- /dev/null
+++ b/cellular/mock_out_of_credits_detector.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 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/mock_out_of_credits_detector.h"
+
+#include <gmock/gmock.h>
+
+namespace shill {
+
+MockOutOfCreditsDetector::MockOutOfCreditsDetector(
+ EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service)
+ : OutOfCreditsDetector(dispatcher, manager, metrics, service) {}
+
+MockOutOfCreditsDetector::~MockOutOfCreditsDetector() {}
+
+} // namespace shill
diff --git a/cellular/mock_out_of_credits_detector.h b/cellular/mock_out_of_credits_detector.h
new file mode 100644
index 0000000..7fed4e5
--- /dev/null
+++ b/cellular/mock_out_of_credits_detector.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_CELLULAR_MOCK_OUT_OF_CREDITS_DETECTOR_H_
+#define SHILL_CELLULAR_MOCK_OUT_OF_CREDITS_DETECTOR_H_
+
+#include <gmock/gmock.h>
+
+#include "shill/cellular/out_of_credits_detector.h"
+
+namespace shill {
+
+class MockOutOfCreditsDetector : public OutOfCreditsDetector {
+ public:
+ MockOutOfCreditsDetector(EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service);
+ ~MockOutOfCreditsDetector() override;
+
+ MOCK_METHOD0(ResetDetector, void());
+ MOCK_CONST_METHOD0(IsDetecting, bool());
+ MOCK_METHOD2(NotifyServiceStateChanged,
+ void(Service::ConnectState old_state,
+ Service::ConnectState new_state));
+ MOCK_METHOD1(NotifySubscriptionStateChanged,
+ void(uint32_t subscription_state));
+ MOCK_CONST_METHOD0(out_of_credits, bool());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockOutOfCreditsDetector);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MOCK_OUT_OF_CREDITS_DETECTOR_H_
diff --git a/cellular/modem.cc b/cellular/modem.cc
new file mode 100644
index 0000000..3076623
--- /dev/null
+++ b/cellular/modem.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 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 <base/bind.h>
+#include <base/strings/stringprintf.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/logging.h"
+#include "shill/manager.h"
+#include "shill/net/rtnl_handler.h"
+#include "shill/proxy_factory.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(Modem *m) { return m->path().c_str(); }
+}
+
+// TODO(petkov): Consider generating these in mm/mm-modem.h.
+const char Modem::kPropertyLinkName[] = "Device";
+const char Modem::kPropertyIPMethod[] = "IpMethod";
+const char Modem::kPropertyType[] = "Type";
+
+// 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 &owner,
+ const string &service,
+ const string &path,
+ ModemInfo * modem_info)
+ : owner_(owner),
+ service_(service),
+ path_(path),
+ modem_info_(modem_info),
+ type_(Cellular::kTypeInvalid),
+ pending_device_info_(false),
+ rtnl_handler_(RTNLHandler::GetInstance()),
+ proxy_factory_(ProxyFactory::GetInstance()) {
+ LOG(INFO) << "Modem created: " << owner << " at " << path;
+}
+
+Modem::~Modem() {
+ LOG(INFO) << "Modem destructed: " << owner_ << " at " << path_;
+ if (device_) {
+ device_->DestroyService();
+ modem_info_->manager()->device_info()->DeregisterDevice(device_);
+ }
+}
+
+void Modem::Init() {
+ dbus_properties_proxy_.reset(
+ proxy_factory_->CreateDBusPropertiesProxy(path(), owner()));
+ dbus_properties_proxy_->set_modem_manager_properties_changed_callback(
+ Bind(&Modem::OnModemManagerPropertiesChanged, Unretained(this)));
+ dbus_properties_proxy_->set_properties_changed_callback(
+ Bind(&Modem::OnDBusPropertiesChanged, Unretained(this)));
+}
+
+void Modem::OnDeviceInfoAvailable(const string &link_name) {
+ SLOG(this, 2) << __func__;
+ if (pending_device_info_ && link_name_ == link_name) {
+ // pending_device_info_ is only set if we've already been through
+ // CreateDeviceFromModemProperties() and saved our initial
+ // properties already
+ pending_device_info_ = false;
+ CreateDeviceFromModemProperties(initial_properties_);
+ }
+}
+
+Cellular *Modem::ConstructCellular(const string &link_name,
+ const string &address,
+ int interface_index) {
+ LOG(INFO) << "Creating a cellular device on link " << link_name
+ << " interface index " << interface_index << ".";
+ return new Cellular(modem_info_,
+ link_name,
+ address,
+ interface_index,
+ type_,
+ owner_,
+ service_,
+ path_,
+ ProxyFactory::GetInstance());
+}
+
+void Modem::CreateDeviceFromModemProperties(
+ const DBusInterfaceToProperties &properties) {
+ SLOG(this, 2) << __func__;
+
+ if (device_.get()) {
+ return;
+ }
+
+ DBusInterfaceToProperties::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)
+ << "No hardware address, device creation pending device info.";
+ initial_properties_ = properties;
+ 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;
+ }
+
+ if (modem_info_->manager()->device_info()->IsDeviceBlackListed(link_name_)) {
+ LOG(INFO) << "Not creating cellular device for blacklisted interface "
+ << link_name_ << ".";
+ return;
+ }
+
+ device_ = ConstructCellular(link_name_, mac_address, interface_index);
+ // Give the device a chance to extract any capability-specific properties.
+ for (properties_it = properties.begin(); properties_it != properties.end();
+ ++properties_it) {
+ device_->OnDBusPropertiesChanged(
+ properties_it->first, properties_it->second, vector<string>());
+ }
+
+ modem_info_->manager()->device_info()->RegisterDevice(device_);
+}
+
+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::OnDBusPropertiesChanged(
+ const string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const vector<string> &invalidated_properties) {
+ SLOG(this, 2) << __func__;
+ SLOG(this, 3) << "PropertiesChanged signal received.";
+ if (device_.get()) {
+ device_->OnDBusPropertiesChanged(interface,
+ changed_properties,
+ invalidated_properties);
+ }
+}
+
+void Modem::OnModemManagerPropertiesChanged(
+ const string &interface,
+ const DBusPropertiesMap &properties) {
+ vector<string> invalidated_properties;
+ OnDBusPropertiesChanged(interface, properties, invalidated_properties);
+}
+
+} // namespace shill
diff --git a/cellular/modem.h b/cellular/modem.h
new file mode 100644
index 0000000..b01ce77
--- /dev/null
+++ b/cellular/modem.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_H_
+#define SHILL_CELLULAR_MODEM_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/files/file_util.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/dbus_objectmanager_proxy_interface.h"
+#include "shill/cellular/modem_info.h"
+#include "shill/dbus_properties_proxy_interface.h"
+#include "shill/refptr_types.h"
+
+namespace shill {
+
+class ProxyFactory;
+
+// Handles an instance of ModemManager.Modem and an instance of a Cellular
+// device.
+class Modem {
+ public:
+ // |owner| is the ModemManager DBus service owner (e.g., ":1.17"). |path| is
+ // the ModemManager.Modem DBus object path (e.g.,
+ // "/org/chromium/ModemManager/Gobi/0").
+ Modem(const std::string &owner,
+ const std::string &service,
+ const std::string &path,
+ ModemInfo *modem_info);
+ virtual ~Modem();
+
+ // Asynchronously initializes support for the modem.
+ // If the |properties| are valid and the MAC address is present,
+ // constructs and registers a Cellular device in |device_| based on
+ // |properties|.
+ virtual void CreateDeviceFromModemProperties(
+ const DBusInterfaceToProperties &properties);
+
+ void OnDeviceInfoAvailable(const std::string &link_name);
+
+ const std::string &owner() const { return owner_; }
+ const std::string &service() const { return service_; }
+ const std::string &path() const { return path_; }
+
+ void set_type(Cellular::Type type) { type_ = type; }
+
+ protected:
+ static const char kPropertyLinkName[];
+ static const char kPropertyIPMethod[];
+ static const char kPropertyType[];
+
+ virtual void Init();
+
+ CellularRefPtr device() const { return device_; }
+
+ virtual Cellular *ConstructCellular(const std::string &link_name,
+ const std::string &device_name,
+ int interface_index);
+ virtual bool GetLinkName(const DBusPropertiesMap &properties,
+ std::string *name) const = 0;
+ // Returns the name of the DBUS Modem interface.
+ virtual std::string GetModemInterface(void) const = 0;
+
+ private:
+ friend class ModemTest;
+ friend class Modem1Test;
+ FRIEND_TEST(Modem1Test, Init);
+ FRIEND_TEST(Modem1Test, CreateDeviceMM1);
+ FRIEND_TEST(ModemManager1Test, Connect);
+ FRIEND_TEST(ModemManager1Test, AddRemoveInterfaces);
+ FRIEND_TEST(ModemManagerClassicTest, Connect);
+ FRIEND_TEST(ModemManagerCoreTest, ShouldAddModem);
+ FRIEND_TEST(ModemTest, CreateDeviceEarlyFailures);
+ FRIEND_TEST(ModemTest, CreateDevicePPP);
+ FRIEND_TEST(ModemTest, EarlyDeviceProperties);
+ FRIEND_TEST(ModemTest, GetDeviceParams);
+ FRIEND_TEST(ModemTest, Init);
+ FRIEND_TEST(ModemTest, PendingDevicePropertiesAndCreate);
+
+ // Constants associated with fake network devices for PPP dongles.
+ // See |fake_dev_serial_|, below, for more info.
+ static constexpr char kFakeDevNameFormat[] = "no_netdev_%zu";
+ static const char kFakeDevAddress[];
+ static const int kFakeDevInterfaceIndex;
+
+ // Find the |mac_address| and |interface_index| for the kernel
+ // network device with name |link_name|. Returns true iff both
+ // |mac_address| and |interface_index| were found. Modifies
+ // |interface_index| even on failure.
+ virtual bool GetDeviceParams(std::string *mac_address, int *interface_index);
+
+ virtual void OnDBusPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &changed_properties,
+ const std::vector<std::string> &invalidated_properties);
+ virtual void OnModemManagerPropertiesChanged(
+ const std::string &interface,
+ const DBusPropertiesMap &properties);
+
+ // A proxy to the org.freedesktop.DBusProperties interface used to obtain
+ // ModemManager.Modem properties and watch for property changes
+ std::unique_ptr<DBusPropertiesProxyInterface> dbus_properties_proxy_;
+
+ DBusInterfaceToProperties initial_properties_;
+
+ const std::string owner_;
+ const std::string service_;
+ const std::string path_;
+
+ CellularRefPtr device_;
+
+ ModemInfo *modem_info_;
+ std::string link_name_;
+ Cellular::Type type_;
+ bool pending_device_info_;
+ RTNLHandler *rtnl_handler_;
+
+ // Store cached copies of singletons for speed/ease of testing.
+ ProxyFactory *proxy_factory_;
+
+ // Serial number used to uniquify fake device names for Cellular
+ // devices that don't have network devices. (Names must be unique
+ // for D-Bus, and PPP dongles don't have network devices.)
+ static size_t fake_dev_serial_;
+
+ DISALLOW_COPY_AND_ASSIGN(Modem);
+};
+
+class ModemClassic : public Modem {
+ public:
+ ModemClassic(const std::string &owner,
+ const std::string &service,
+ const std::string &path,
+ ModemInfo *modem_info);
+ ~ModemClassic() override;
+
+ // Gathers information and passes it to CreateDeviceFromModemProperties.
+ void CreateDeviceClassic(const DBusPropertiesMap &modem_properties);
+
+ protected:
+ virtual bool GetLinkName(const DBusPropertiesMap &modem_properties,
+ std::string *name) const;
+ virtual std::string GetModemInterface(void) const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ModemClassic);
+};
+
+class Modem1 : public Modem {
+ public:
+ Modem1(const std::string &owner,
+ const std::string &service,
+ const std::string &path,
+ ModemInfo *modem_info);
+ ~Modem1() override;
+
+ // Gathers information and passes it to CreateDeviceFromModemProperties.
+ void CreateDeviceMM1(const DBusInterfaceToProperties &properties);
+
+ protected:
+ virtual bool GetLinkName(const DBusPropertiesMap &modem_properties,
+ std::string *name) const;
+ virtual std::string GetModemInterface(void) const;
+
+ private:
+ friend class Modem1Test;
+
+ DISALLOW_COPY_AND_ASSIGN(Modem1);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_H_
diff --git a/cellular/modem_1.cc b/cellular/modem_1.cc
new file mode 100644
index 0000000..c62b56d
--- /dev/null
+++ b/cellular/modem_1.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2013 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 <ModemManager/ModemManager.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/device_info.h"
+
+using std::string;
+using std::vector;
+
+namespace shill {
+
+Modem1::Modem1(const string &owner,
+ const string &service,
+ const string &path,
+ ModemInfo *modem_info)
+ : Modem(owner, service, path, modem_info) {}
+
+Modem1::~Modem1() {}
+
+bool Modem1::GetLinkName(const DBusPropertiesMap &modem_props,
+ string *name) const {
+ DBusPropertiesMap::const_iterator props_it;
+ string net_port;
+
+ props_it = modem_props.find(MM_MODEM_PROPERTY_PORTS);
+ if (props_it == modem_props.end()) {
+ LOG(ERROR) << "Device missing property: " << MM_MODEM_PROPERTY_PORTS;
+ return false;
+ }
+
+ vector<DBus::Struct<string, uint32_t>> ports = props_it->second;
+ for (const auto &port_pair : ports) {
+ if (port_pair._2 == MM_MODEM_PORT_TYPE_NET) {
+ net_port = port_pair._1;
+ break;
+ }
+ }
+
+ if (net_port.empty()) {
+ LOG(ERROR) << "Could not find net port used by the device.";
+ return false;
+ }
+
+ *name = net_port;
+ return true;
+}
+
+void Modem1::CreateDeviceMM1(const DBusInterfaceToProperties &properties) {
+ Init();
+ uint32_t capabilities = kuint32max;
+ DBusInterfaceToProperties::const_iterator it =
+ properties.find(MM_DBUS_INTERFACE_MODEM);
+ if (it == properties.end()) {
+ LOG(ERROR) << "Cellular device with no modem properties";
+ return;
+ }
+ const DBusPropertiesMap &modem_props = it->second;
+ DBusProperties::GetUint32(modem_props,
+ MM_MODEM_PROPERTY_CURRENTCAPABILITIES,
+ &capabilities);
+
+ if ((capabilities & MM_MODEM_CAPABILITY_LTE) ||
+ (capabilities & MM_MODEM_CAPABILITY_GSM_UMTS)) {
+ set_type(Cellular::kTypeUniversal);
+ } else if (capabilities & MM_MODEM_CAPABILITY_CDMA_EVDO) {
+ set_type(Cellular::kTypeUniversalCDMA);
+ } 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);
+}
+
+string Modem1::GetModemInterface(void) const {
+ return string(MM_DBUS_INTERFACE_MODEM);
+}
+
+} // namespace shill
diff --git a/cellular/modem_1_unittest.cc b/cellular/modem_1_unittest.cc
new file mode 100644
index 0000000..44ac752
--- /dev/null
+++ b/cellular/modem_1_unittest.cc
@@ -0,0 +1,132 @@
+// Copyright (c) 2012 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 <base/files/scoped_temp_dir.h>
+#include <ModemManager/ModemManager.h>
+
+#include "shill/cellular/cellular_capability.h"
+#include "shill/cellular/mock_cellular.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/dbus_property_matchers.h"
+#include "shill/event_dispatcher.h"
+#include "shill/manager.h"
+#include "shill/mock_dbus_properties_proxy.h"
+#include "shill/mock_device_info.h"
+#include "shill/mock_proxy_factory.h"
+#include "shill/net/mock_rtnl_handler.h"
+#include "shill/net/rtnl_handler.h"
+#include "shill/testing.h"
+
+using base::FilePath;
+using std::string;
+using std::vector;
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::Test;
+
+namespace shill {
+
+namespace {
+
+const int kTestInterfaceIndex = 5;
+const char kLinkName[] = "usb0";
+const char kOwner[] = ":1.18";
+const char kService[] = "org.chromium.ModemManager";
+const char kPath[] = "/org/chromium/ModemManager/Gobi/0";
+const unsigned char kAddress[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+
+} // namespace
+
+class Modem1Test : public Test {
+ public:
+ Modem1Test()
+ : modem_info_(nullptr, &dispatcher_, nullptr, nullptr, nullptr),
+ device_info_(modem_info_.control_interface(), modem_info_.dispatcher(),
+ modem_info_.metrics(), modem_info_.manager()),
+ proxy_(new MockDBusPropertiesProxy()),
+ modem_(
+ new Modem1(
+ kOwner,
+ kService,
+ kPath,
+ &modem_info_)) {}
+ virtual void SetUp();
+ virtual void TearDown();
+
+ void ReplaceSingletons() {
+ modem_->rtnl_handler_ = &rtnl_handler_;
+ modem_->proxy_factory_ = &proxy_factory_;
+ }
+
+ protected:
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ MockDeviceInfo device_info_;
+ std::unique_ptr<MockDBusPropertiesProxy> proxy_;
+ MockProxyFactory proxy_factory_;
+ std::unique_ptr<Modem1> modem_;
+ MockRTNLHandler rtnl_handler_;
+ ByteString expected_address_;
+};
+
+void Modem1Test::SetUp() {
+ EXPECT_EQ(kOwner, modem_->owner_);
+ EXPECT_EQ(kService, modem_->service_);
+ EXPECT_EQ(kPath, modem_->path_);
+ ReplaceSingletons();
+ expected_address_ = ByteString(kAddress, arraysize(kAddress));
+
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(kLinkName)).
+ WillRepeatedly(Return(kTestInterfaceIndex));
+
+ EXPECT_CALL(*modem_info_.mock_manager(), device_info())
+ .WillRepeatedly(Return(&device_info_));
+ EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _)).
+ WillOnce(DoAll(SetArgumentPointee<1>(expected_address_),
+ Return(true)));
+}
+
+void Modem1Test::TearDown() {
+ modem_.reset();
+}
+
+TEST_F(Modem1Test, CreateDeviceMM1) {
+ DBusInterfaceToProperties properties;
+ DBusPropertiesMap modem_properties;
+ DBus::Variant lock;
+ lock.writer().append_uint32(MM_MODEM_LOCK_NONE);
+ modem_properties[MM_MODEM_PROPERTY_UNLOCKREQUIRED] = lock;
+
+ DBus::Variant ports_variant;
+ DBus::MessageIter ports_message_iter = ports_variant.writer();
+ DBus::MessageIter ports_array_iter = ports_message_iter.new_array("(su)");
+ DBus::MessageIter port_struct_iter = ports_array_iter.new_struct();
+ port_struct_iter.append_string(kLinkName);
+ port_struct_iter.append_uint32(MM_MODEM_PORT_TYPE_NET);
+ ports_array_iter.close_container(port_struct_iter);
+ ports_message_iter.close_container(ports_array_iter);
+ modem_properties[MM_MODEM_PROPERTY_PORTS] = ports_variant;
+
+ properties[MM_DBUS_INTERFACE_MODEM] = modem_properties;
+
+ DBusPropertiesMap modem3gpp_properties;
+ DBus::Variant registration_state_variant;
+ registration_state_variant.writer().append_uint32(
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME);
+ modem3gpp_properties[MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE] =
+ registration_state_variant;
+ properties[MM_DBUS_INTERFACE_MODEM_MODEM3GPP] = modem3gpp_properties;
+
+ EXPECT_CALL(proxy_factory_, CreateDBusPropertiesProxy(kPath, kOwner))
+ .WillOnce(ReturnAndReleasePointee(&proxy_));
+ modem_->CreateDeviceMM1(properties);
+ EXPECT_TRUE(modem_->device().get());
+ EXPECT_TRUE(modem_->device()->capability_->IsRegistered());
+}
+
+} // namespace shill
diff --git a/cellular/modem_cdma_proxy.cc b/cellular/modem_cdma_proxy.cc
new file mode 100644
index 0000000..3cfd275
--- /dev/null
+++ b/cellular/modem_cdma_proxy.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 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_cdma_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+ModemCDMAProxy::ModemCDMAProxy(
+ DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemCDMAProxy::~ModemCDMAProxy() {}
+
+void ModemCDMAProxy::Activate(const string &carrier, Error *error,
+ const ActivationResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::ActivateAsync, callback,
+ error, &CellularError::FromDBusError, timeout,
+ carrier);
+}
+
+void ModemCDMAProxy::GetRegistrationState(
+ Error *error,
+ const RegistrationStateCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetRegistrationStateAsync,
+ callback, error, &CellularError::FromDBusError, timeout);
+}
+
+void ModemCDMAProxy::GetSignalQuality(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetSignalQualityAsync, callback,
+ error, &CellularError::FromDBusError, timeout);
+}
+
+const string ModemCDMAProxy::MEID() {
+ LOG(INFO) << "ModemCDMAProxy::" << __func__;
+ SLOG(&proxy_.path(), 2) << __func__;
+ try {
+ return proxy_.Meid();
+ } catch (const DBus::Error &e) {
+ LOG(FATAL) << "DBus exception: " << e.name() << ": " << e.what();
+ return string(); // Make the compiler happy.
+ }
+}
+
+void ModemCDMAProxy::set_activation_state_callback(
+ const ActivationStateSignalCallback &callback) {
+ proxy_.set_activation_state_callback(callback);
+}
+
+void ModemCDMAProxy::set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback) {
+ proxy_.set_signal_quality_callback(callback);
+}
+
+void ModemCDMAProxy::set_registration_state_callback(
+ const RegistrationStateSignalCallback &callback) {
+ proxy_.set_registration_state_callback(callback);
+}
+
+ModemCDMAProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemCDMAProxy::Proxy::~Proxy() {}
+
+void ModemCDMAProxy::Proxy::set_activation_state_callback(
+ const ActivationStateSignalCallback &callback) {
+ activation_state_callback_ = callback;
+}
+
+void ModemCDMAProxy::Proxy::set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback) {
+ signal_quality_callback_ = callback;
+}
+
+void ModemCDMAProxy::Proxy::set_registration_state_callback(
+ const RegistrationStateSignalCallback &callback) {
+ registration_state_callback_ = callback;
+}
+
+void ModemCDMAProxy::Proxy::ActivationStateChanged(
+ const uint32_t &activation_state,
+ const uint32_t &activation_error,
+ const DBusPropertiesMap &status_changes) {
+ SLOG(&path(), 2) << __func__ << "(" << activation_state << ", "
+ << activation_error << ")";
+ activation_state_callback_.Run(activation_state,
+ activation_error,
+ status_changes);
+}
+
+void ModemCDMAProxy::Proxy::SignalQuality(const uint32_t &quality) {
+ SLOG(&path(), 2) << __func__ << "(" << quality << ")";
+ signal_quality_callback_.Run(quality);
+}
+
+void ModemCDMAProxy::Proxy::RegistrationStateChanged(
+ const uint32_t &cdma_1x_state,
+ const uint32_t &evdo_state) {
+ SLOG(&path(), 2) << __func__ << "(" << cdma_1x_state << ", "
+ << evdo_state << ")";
+ registration_state_callback_.Run(cdma_1x_state, evdo_state);
+}
+
+void ModemCDMAProxy::Proxy::ActivateCallback(const uint32_t &status,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__ << "(" << status << ")";
+ unique_ptr<ActivationResultCallback> callback(
+ reinterpret_cast<ActivationResultCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(status, error);
+}
+
+void ModemCDMAProxy::Proxy::GetRegistrationStateCallback(
+ const uint32_t &state_1x, const uint32_t &state_evdo,
+ const DBus::Error &dberror, void *data) {
+ SLOG(&path(), 2) << __func__ << "(" << state_1x << ", " << state_evdo << ")";
+ unique_ptr<RegistrationStateCallback> callback(
+ reinterpret_cast<RegistrationStateCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(state_1x, state_evdo, error);
+}
+
+
+void ModemCDMAProxy::Proxy::GetSignalQualityCallback(const uint32_t &quality,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__ << "(" << quality << ")";
+ unique_ptr<SignalQualityCallback> callback(
+ reinterpret_cast<SignalQualityCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(quality, error);
+}
+
+} // namespace shill
diff --git a/cellular/modem_cdma_proxy.h b/cellular/modem_cdma_proxy.h
new file mode 100644
index 0000000..68b75d8
--- /dev/null
+++ b/cellular/modem_cdma_proxy.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_CDMA_PROXY_H_
+#define SHILL_CELLULAR_MODEM_CDMA_PROXY_H_
+
+#include <string>
+
+#include "dbus_proxies/org.freedesktop.ModemManager.Modem.Cdma.h"
+#include "shill/cellular/modem_cdma_proxy_interface.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+
+// A proxy to (old) ModemManager.Modem.CDMA.
+class ModemCDMAProxy : public ModemCDMAProxyInterface {
+ public:
+ // Constructs a ModemManager.Modem.CDMA DBus object proxy at |path| owned by
+ // |service|.
+ ModemCDMAProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemCDMAProxy() override;
+
+ // Inherited from ModemCDMAProxyInterface.
+ virtual void Activate(const std::string &carrier, Error *error,
+ const ActivationResultCallback &callback, int timeout);
+ virtual void GetRegistrationState(Error *error,
+ const RegistrationStateCallback &callback,
+ int timeout);
+ virtual void GetSignalQuality(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout);
+ virtual const std::string MEID();
+
+ virtual void set_activation_state_callback(
+ const ActivationStateSignalCallback &callback);
+ virtual void set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback);
+ virtual void set_registration_state_callback(
+ const RegistrationStateSignalCallback &callback);
+
+ public:
+ class Proxy
+ : public org::freedesktop::ModemManager::Modem::Cdma_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ void set_activation_state_callback(
+ const ActivationStateSignalCallback &callback);
+ void set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback);
+ void set_registration_state_callback(
+ const RegistrationStateSignalCallback &callback);
+
+ private:
+ // Signal callbacks inherited from ModemManager::Modem::Cdma_proxy.
+ virtual void ActivationStateChanged(
+ const uint32_t &activation_state,
+ const uint32_t &activation_error,
+ const DBusPropertiesMap &status_changes);
+ virtual void SignalQuality(const uint32_t &quality);
+ virtual void RegistrationStateChanged(const uint32_t &cdma_1x_state,
+ const uint32_t &evdo_state);
+
+ // Method callbacks inherited from ModemManager::Modem::Cdma_proxy.
+ virtual void ActivateCallback(const uint32_t &status,
+ const DBus::Error &dberror, void *data);
+ virtual void GetRegistrationStateCallback(const uint32_t &state_1x,
+ const uint32_t &state_evdo,
+ const DBus::Error &error,
+ void *data);
+ virtual void GetSignalQualityCallback(const uint32_t &quality,
+ const DBus::Error &dberror,
+ void *data);
+
+ ActivationStateSignalCallback activation_state_callback_;
+ SignalQualitySignalCallback signal_quality_callback_;
+ RegistrationStateSignalCallback registration_state_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ModemCDMAProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_CDMA_PROXY_H_
diff --git a/cellular/modem_cdma_proxy_interface.h b/cellular/modem_cdma_proxy_interface.h
new file mode 100644
index 0000000..481b412
--- /dev/null
+++ b/cellular/modem_cdma_proxy_interface.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 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.
+
+#ifndef SHILL_CELLULAR_MODEM_CDMA_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MODEM_CDMA_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+
+class Error;
+
+typedef base::Callback<void(uint32_t)> SignalQualitySignalCallback;
+typedef base::Callback<void(uint32_t, uint32_t)>
+ RegistrationStateSignalCallback;
+
+typedef base::Callback<void(uint32_t, const Error &)> ActivationResultCallback;
+typedef base::Callback<void(uint32_t, const Error &)> SignalQualityCallback;
+typedef base::Callback<void(uint32_t, uint32_t,
+ const Error &)> RegistrationStateCallback;
+
+// These are the methods that a ModemManager.Modem.CDMA proxy must support.
+// The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously. Call completion is signalled via
+// the callbacks passed to the methods.
+class ModemCDMAProxyInterface {
+ public:
+ virtual ~ModemCDMAProxyInterface() {}
+
+ virtual void Activate(const std::string &carrier, Error *error,
+ const ActivationResultCallback &callback,
+ int timeout) = 0;
+ virtual void GetRegistrationState(Error *error,
+ const RegistrationStateCallback &callback,
+ int timeout) = 0;
+ virtual void GetSignalQuality(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout) = 0;
+
+ // Properties.
+ virtual const std::string MEID() = 0;
+
+ virtual void set_activation_state_callback(
+ const ActivationStateSignalCallback &callback) = 0;
+ virtual void set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback) = 0;
+ virtual void set_registration_state_callback(
+ const RegistrationStateSignalCallback &callback) = 0;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_CDMA_PROXY_INTERFACE_H_
diff --git a/cellular/modem_classic.cc b/cellular/modem_classic.cc
new file mode 100644
index 0000000..81ffb88
--- /dev/null
+++ b/cellular/modem_classic.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 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 <mm/mm-modem.h>
+
+#include "shill/cellular/cellular.h"
+
+using std::string;
+using std::vector;
+
+namespace shill {
+
+ModemClassic::ModemClassic(const string &owner,
+ const string &service,
+ const string &path,
+ ModemInfo *modem_info)
+ : Modem(owner, service, path, modem_info) {}
+
+ModemClassic::~ModemClassic() {}
+
+bool ModemClassic::GetLinkName(const DBusPropertiesMap& modem_properties,
+ string *name) const {
+ return DBusProperties::GetString(modem_properties, kPropertyLinkName, name);
+}
+
+void ModemClassic::CreateDeviceClassic(
+ const DBusPropertiesMap &modem_properties) {
+ Init();
+ uint32_t mm_type = kuint32max;
+ DBusProperties::GetUint32(modem_properties, kPropertyType, &mm_type);
+ switch (mm_type) {
+ case MM_MODEM_TYPE_CDMA:
+ set_type(Cellular::kTypeCDMA);
+ break;
+ case MM_MODEM_TYPE_GSM:
+ set_type(Cellular::kTypeGSM);
+ break;
+ default:
+ LOG(ERROR) << "Unsupported cellular modem type: " << mm_type;
+ return;
+ }
+ uint32_t ip_method = kuint32max;
+ if (!DBusProperties::GetUint32(modem_properties,
+ kPropertyIPMethod,
+ &ip_method) ||
+ ip_method != MM_MODEM_IP_METHOD_DHCP) {
+ LOG(ERROR) << "Unsupported IP method: " << ip_method;
+ return;
+ }
+
+ DBusInterfaceToProperties properties;
+ properties[MM_MODEM_INTERFACE] = modem_properties;
+ CreateDeviceFromModemProperties(properties);
+}
+
+string ModemClassic::GetModemInterface(void) const {
+ return string(MM_MODEM_INTERFACE);
+}
+
+} // namespace shill
diff --git a/cellular/modem_gobi_proxy.cc b/cellular/modem_gobi_proxy.cc
new file mode 100644
index 0000000..eeba73c
--- /dev/null
+++ b/cellular/modem_gobi_proxy.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 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_gobi_proxy.h"
+
+#include <memory>
+
+#include <base/bind.h>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+
+using base::Bind;
+using base::Callback;
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+ModemGobiProxy::ModemGobiProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemGobiProxy::~ModemGobiProxy() {}
+
+void ModemGobiProxy::SetCarrier(const string &carrier,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::SetCarrierAsync, callback,
+ error, &CellularError::FromDBusError, timeout,
+ carrier);
+}
+
+ModemGobiProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemGobiProxy::Proxy::~Proxy() {}
+
+void ModemGobiProxy::Proxy::SetCarrierCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(error);
+}
+
+} // namespace shill
diff --git a/cellular/modem_gobi_proxy.h b/cellular/modem_gobi_proxy.h
new file mode 100644
index 0000000..41157ae
--- /dev/null
+++ b/cellular/modem_gobi_proxy.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_GOBI_PROXY_H_
+#define SHILL_CELLULAR_MODEM_GOBI_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "shill/cellular/modem_gobi_proxy_interface.h"
+#include "shill/dbus_proxies/modem-gobi.h"
+
+namespace shill {
+
+// A proxy to (old) ModemManager.Modem.Gobi.
+class ModemGobiProxy : public ModemGobiProxyInterface {
+ public:
+ // Constructs a ModemManager.Modem.Gobi DBus object proxy at |path| owned by
+ // |service|.
+ ModemGobiProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemGobiProxy() override;
+
+ // Inherited from ModemGobiProxyInterface.
+ virtual void SetCarrier(const std::string &carrier,
+ Error *error, const ResultCallback &callback,
+ int timeout);
+
+ private:
+ class Proxy
+ : public org::chromium::ModemManager::Modem::Gobi_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Signal callbacks inherited from ModemManager::Modem::Gobi_proxy.
+ // [None]
+
+ // Method callbacks inherited from ModemManager::Modem::Gobi_proxy.
+ virtual void SetCarrierCallback(const DBus::Error &dberror, void *data);
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemGobiProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_GOBI_PROXY_H_
diff --git a/cellular/modem_gobi_proxy_interface.h b/cellular/modem_gobi_proxy_interface.h
new file mode 100644
index 0000000..dfc8cd8
--- /dev/null
+++ b/cellular/modem_gobi_proxy_interface.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_GOBI_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MODEM_GOBI_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+
+class Error;
+
+// These are the methods that a ModemManager.Modem.Gobi proxy must support. The
+// interface is provided so that it can be mocked in tests. All calls are made
+// asynchronously.
+class ModemGobiProxyInterface {
+ public:
+ virtual ~ModemGobiProxyInterface() {}
+
+ virtual void SetCarrier(const std::string &carrier,
+ Error *error, const ResultCallback &callback,
+ int timeout) = 0;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_GOBI_PROXY_INTERFACE_H_
diff --git a/cellular/modem_gsm_card_proxy.cc b/cellular/modem_gsm_card_proxy.cc
new file mode 100644
index 0000000..a60fe9c
--- /dev/null
+++ b/cellular/modem_gsm_card_proxy.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2012 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_gsm_card_proxy.h"
+
+#include <memory>
+
+#include <base/bind.h>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+
+using base::Bind;
+using base::Callback;
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+template<typename TraceMsgT, typename CallT, typename CallbackT,
+ typename... ArgTypes>
+void ModemGSMCardProxy::BeginCall(
+ const TraceMsgT &trace_msg, const CallT &call, const CallbackT &callback,
+ Error *error, int timeout, ArgTypes... rest) {
+ BeginAsyncDBusCall(trace_msg, proxy_, call, callback, error,
+ &CellularError::FromDBusError, timeout, rest...);
+}
+
+ModemGSMCardProxy::ModemGSMCardProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemGSMCardProxy::~ModemGSMCardProxy() {}
+
+void ModemGSMCardProxy::GetIMEI(Error *error,
+ const GSMIdentifierCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::GetImeiAsync, callback, error, timeout);
+}
+
+void ModemGSMCardProxy::GetIMSI(Error *error,
+ const GSMIdentifierCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::GetImsiAsync, callback, error, timeout);
+}
+
+void ModemGSMCardProxy::GetSPN(Error *error,
+ const GSMIdentifierCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::GetSpnAsync, callback, error, timeout);
+}
+
+void ModemGSMCardProxy::GetMSISDN(Error *error,
+ const GSMIdentifierCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::GetMsIsdnAsync, callback, error, timeout);
+}
+
+void ModemGSMCardProxy::EnablePIN(const string &pin, bool enabled,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::EnablePinAsync, callback, error, timeout,
+ pin, enabled);
+}
+
+void ModemGSMCardProxy::SendPIN(const string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::SendPinAsync, callback, error, timeout,
+ pin);
+}
+
+void ModemGSMCardProxy::SendPUK(const string &puk, const string &pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::SendPukAsync, callback, error, timeout,
+ puk, pin);
+}
+
+void ModemGSMCardProxy::ChangePIN(const string &old_pin,
+ const string &new_pin,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginCall(__func__, &Proxy::ChangePinAsync, callback, error, timeout,
+ old_pin, new_pin);
+}
+
+uint32_t ModemGSMCardProxy::EnabledFacilityLocks() {
+ SLOG(&proxy_.path(), 2) << __func__;
+ try {
+ return proxy_.EnabledFacilityLocks();
+ } catch (const DBus::Error &e) {
+ LOG(FATAL) << "DBus exception: " << e.name() << ": " << e.what();
+ return 0; // Make the compiler happy.
+ }
+}
+
+ModemGSMCardProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+void ModemGSMCardProxy::Proxy::GetIdCallback(const string &id,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<GSMIdentifierCallback> callback(
+ reinterpret_cast<GSMIdentifierCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(id, error);
+}
+
+void ModemGSMCardProxy::Proxy::GetImeiCallback(const string &imei,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ GetIdCallback(imei, dberror, data);
+}
+
+void ModemGSMCardProxy::Proxy::GetImsiCallback(const string &imsi,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ GetIdCallback(imsi, dberror, data);
+}
+
+void ModemGSMCardProxy::Proxy::GetSpnCallback(const string &spn,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ GetIdCallback(spn, dberror, data);
+}
+
+void ModemGSMCardProxy::Proxy::GetMsIsdnCallback(const string &msisdn,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ GetIdCallback(msisdn, dberror, data);
+}
+
+void ModemGSMCardProxy::Proxy::PinCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemGSMCardProxy::Proxy::EnablePinCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ PinCallback(dberror, data);
+}
+
+void ModemGSMCardProxy::Proxy::SendPinCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ PinCallback(dberror, data);
+}
+
+void ModemGSMCardProxy::Proxy::SendPukCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ PinCallback(dberror, data);
+}
+
+void ModemGSMCardProxy::Proxy::ChangePinCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ PinCallback(dberror, data);
+}
+
+ModemGSMCardProxy::Proxy::~Proxy() {}
+
+} // namespace shill
diff --git a/cellular/modem_gsm_card_proxy.h b/cellular/modem_gsm_card_proxy.h
new file mode 100644
index 0000000..c26561b
--- /dev/null
+++ b/cellular/modem_gsm_card_proxy.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_GSM_CARD_PROXY_H_
+#define SHILL_CELLULAR_MODEM_GSM_CARD_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "dbus_proxies/org.freedesktop.ModemManager.Modem.Gsm.Card.h"
+#include "shill/cellular/modem_gsm_card_proxy_interface.h"
+
+namespace shill {
+
+// A proxy to (old) ModemManager.Modem.Gsm.Card.
+class ModemGSMCardProxy : public ModemGSMCardProxyInterface {
+ public:
+ // Constructs a ModemManager.Modem.Gsm.Card DBus
+ // object proxy at |path| owned by |service|.
+ ModemGSMCardProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemGSMCardProxy() override;
+
+ // Inherited from ModemGSMCardProxyInterface.
+ virtual void GetIMEI(Error *error, const GSMIdentifierCallback &callback,
+ int timeout);
+ virtual void GetIMSI(Error *error, const GSMIdentifierCallback &callback,
+ int timeout);
+ virtual void GetSPN(Error *error, const GSMIdentifierCallback &callback,
+ int timeout);
+ virtual void GetMSISDN(Error *error, const GSMIdentifierCallback &callback,
+ int timeout);
+ virtual void EnablePIN(const std::string &pin, bool enabled,
+ Error *error, const ResultCallback &callback,
+ int timeout);
+ virtual void SendPIN(const std::string &pin,
+ Error *error, const ResultCallback &callback,
+ int timeout);
+ virtual void SendPUK(const std::string &puk, const std::string &pin,
+ Error *error, const ResultCallback &callback,
+ int timeout);
+ virtual void ChangePIN(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error, const ResultCallback &callback,
+ int timeout);
+ virtual uint32_t EnabledFacilityLocks();
+
+ private:
+ class Proxy
+ : public org::freedesktop::ModemManager::Modem::Gsm::Card_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Signal callbacks inherited from ModemManager::Modem::Gsm::Card_proxy.
+ // [None]
+
+ // Method callbacks inherited from ModemManager::Modem::Gsm::Card_proxy.
+ virtual void GetImeiCallback(const std::string &imei,
+ const DBus::Error &dberror, void *data);
+ virtual void GetImsiCallback(const std::string &imsi,
+ const DBus::Error &dberror, void *data);
+ virtual void GetSpnCallback(const std::string &spn,
+ const DBus::Error &dberror, void *data);
+ virtual void GetMsIsdnCallback(const std::string &msisdn,
+ const DBus::Error &dberror, void *data);
+ virtual void EnablePinCallback(const DBus::Error &dberror, void *data);
+ virtual void SendPinCallback(const DBus::Error &dberror, void *data);
+ virtual void SendPukCallback(const DBus::Error &dberror, void *data);
+ virtual void ChangePinCallback(const DBus::Error &dberror, void *data);
+
+ virtual void GetIdCallback(const std::string &id,
+ const DBus::Error &dberror, void *data);
+ virtual void PinCallback(const DBus::Error &dberror, void *data);
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ template<typename TraceMsgT, typename CallT, typename CallbackT,
+ typename... ArgTypes>
+ void BeginCall(
+ const TraceMsgT &trace_msg, const CallT &call,
+ const CallbackT &callback, Error *error, int timeout,
+ ArgTypes... rest);
+
+ DISALLOW_COPY_AND_ASSIGN(ModemGSMCardProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_GSM_CARD_PROXY_H_
diff --git a/cellular/modem_gsm_card_proxy_interface.h b/cellular/modem_gsm_card_proxy_interface.h
new file mode 100644
index 0000000..df0b2e0
--- /dev/null
+++ b/cellular/modem_gsm_card_proxy_interface.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_GSM_CARD_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MODEM_GSM_CARD_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+
+class Error;
+typedef base::Callback<void(const std::string &,
+ const Error &)> GSMIdentifierCallback;
+
+// These are the methods that a ModemManager.Modem.Gsm.Card proxy must
+// support. The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously.
+class ModemGSMCardProxyInterface {
+ public:
+ virtual ~ModemGSMCardProxyInterface() {}
+
+ virtual void GetIMEI(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) = 0;
+ virtual void GetIMSI(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) = 0;
+ virtual void GetSPN(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) = 0;
+ virtual void GetMSISDN(Error *error, const GSMIdentifierCallback &callback,
+ int timeout) = 0;
+ virtual void EnablePIN(const std::string &pin, bool enabled,
+ Error *error, const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void SendPIN(const std::string &pin,
+ Error *error, const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void SendPUK(const std::string &puk, const std::string &pin,
+ Error *error, const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void ChangePIN(const std::string &old_pin,
+ const std::string &new_pin,
+ Error *error, const ResultCallback &callback,
+ int timeout) = 0;
+
+ // Properties.
+ virtual uint32_t EnabledFacilityLocks() = 0;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_GSM_CARD_PROXY_INTERFACE_H_
diff --git a/cellular/modem_gsm_network_proxy.cc b/cellular/modem_gsm_network_proxy.cc
new file mode 100644
index 0000000..4edfe00
--- /dev/null
+++ b/cellular/modem_gsm_network_proxy.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2012 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_gsm_network_proxy.h"
+
+#include <memory>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+
+using base::Callback;
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+ModemGSMNetworkProxy::ModemGSMNetworkProxy(
+ DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemGSMNetworkProxy::~ModemGSMNetworkProxy() {}
+
+void ModemGSMNetworkProxy::GetRegistrationInfo(
+ Error *error,
+ const RegistrationInfoCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetRegistrationInfoAsync,
+ callback, error, &CellularError::FromDBusError, timeout);
+}
+
+void ModemGSMNetworkProxy::GetSignalQuality(
+ Error *error,
+ const SignalQualityCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetSignalQualityAsync, callback,
+ error, &CellularError::FromDBusError, timeout);
+}
+
+void ModemGSMNetworkProxy::Register(const string &network_id,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::RegisterAsync, callback,
+ error, &CellularError::FromDBusError, timeout,
+ network_id);
+}
+
+void ModemGSMNetworkProxy::Scan(Error *error,
+ const ScanResultsCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::ScanAsync, callback,
+ error, &CellularError::FromDBusError, timeout);
+}
+
+uint32_t ModemGSMNetworkProxy::AccessTechnology() {
+ SLOG(&proxy_.path(), 2) << __func__;
+ try {
+ return proxy_.AccessTechnology();
+ } catch (const DBus::Error &e) {
+ LOG(FATAL) << "DBus exception: " << e.name() << ": " << e.what();
+ return 0; // Make the compiler happy.
+ }
+}
+
+void ModemGSMNetworkProxy::set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback) {
+ proxy_.set_signal_quality_callback(callback);
+}
+
+void ModemGSMNetworkProxy::set_network_mode_callback(
+ const NetworkModeSignalCallback &callback) {
+ proxy_.set_network_mode_callback(callback);
+}
+
+void ModemGSMNetworkProxy::set_registration_info_callback(
+ const RegistrationInfoSignalCallback &callback) {
+ proxy_.set_registration_info_callback(callback);
+}
+
+ModemGSMNetworkProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemGSMNetworkProxy::Proxy::~Proxy() {}
+
+void ModemGSMNetworkProxy::Proxy::set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback) {
+ signal_quality_callback_ = callback;
+}
+
+void ModemGSMNetworkProxy::Proxy::set_network_mode_callback(
+ const NetworkModeSignalCallback &callback) {
+ network_mode_callback_ = callback;
+}
+
+void ModemGSMNetworkProxy::Proxy::set_registration_info_callback(
+ const RegistrationInfoSignalCallback &callback) {
+ registration_info_callback_ = callback;
+}
+
+void ModemGSMNetworkProxy::Proxy::SignalQuality(const uint32_t &quality) {
+ SLOG(&path(), 2) << __func__ << "(" << quality << ")";
+ if (!signal_quality_callback_.is_null())
+ signal_quality_callback_.Run(quality);
+}
+
+void ModemGSMNetworkProxy::Proxy::RegistrationInfo(
+ const uint32_t &status,
+ const string &operator_code,
+ const string &operator_name) {
+ SLOG(&path(), 2) << __func__ << "(" << status << ", " << operator_code << ", "
+ << operator_name << ")";
+ if (!registration_info_callback_.is_null())
+ registration_info_callback_.Run(status, operator_code, operator_name);
+}
+
+void ModemGSMNetworkProxy::Proxy::NetworkMode(const uint32_t &mode) {
+ SLOG(&path(), 2) << __func__ << "(" << mode << ")";
+ if (!network_mode_callback_.is_null())
+ network_mode_callback_.Run(mode);
+}
+
+void ModemGSMNetworkProxy::Proxy::RegisterCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemGSMNetworkProxy::Proxy::GetRegistrationInfoCallback(
+ const GSMRegistrationInfo &info, const DBus::Error &dberror, void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<RegistrationInfoCallback> callback(
+ reinterpret_cast<RegistrationInfoCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(info._1, info._2, info._3, error);
+}
+
+void ModemGSMNetworkProxy::Proxy::GetSignalQualityCallback(
+ const uint32_t &quality, const DBus::Error &dberror, void *data) {
+ SLOG(&path(), 2) << __func__ << "(" << quality << ")";
+ unique_ptr<SignalQualityCallback> callback(
+ reinterpret_cast<SignalQualityCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(quality, error);
+}
+
+void ModemGSMNetworkProxy::Proxy::ScanCallback(const GSMScanResults &results,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ScanResultsCallback> callback(
+ reinterpret_cast<ScanResultsCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(results, error);
+}
+
+} // namespace shill
diff --git a/cellular/modem_gsm_network_proxy.h b/cellular/modem_gsm_network_proxy.h
new file mode 100644
index 0000000..15fc385
--- /dev/null
+++ b/cellular/modem_gsm_network_proxy.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_GSM_NETWORK_PROXY_H_
+#define SHILL_CELLULAR_MODEM_GSM_NETWORK_PROXY_H_
+
+#include <string>
+
+#include "dbus_proxies/org.freedesktop.ModemManager.Modem.Gsm.Network.h"
+#include "shill/cellular/modem_gsm_network_proxy_interface.h"
+
+namespace shill {
+
+// A proxy to (old) ModemManager.Modem.Gsm.Network.
+class ModemGSMNetworkProxy : public ModemGSMNetworkProxyInterface {
+ public:
+ // Constructs a ModemManager.Modem.Gsm.Network DBus object proxy at |path|
+ // owned by |service|.
+ ModemGSMNetworkProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemGSMNetworkProxy() override;
+
+ // Inherited from ModemGSMNetworkProxyInterface.
+ virtual void GetRegistrationInfo(Error *error,
+ const RegistrationInfoCallback &callback,
+ int timeout);
+ virtual void GetSignalQuality(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout);
+ virtual void Register(const std::string &network_id,
+ Error *error, const ResultCallback &callback,
+ int timeout);
+ virtual void Scan(Error *error, const ScanResultsCallback &callback,
+ int timeout);
+ virtual uint32_t AccessTechnology();
+
+ virtual void set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback);
+ virtual void set_network_mode_callback(
+ const NetworkModeSignalCallback &callback);
+ virtual void set_registration_info_callback(
+ const RegistrationInfoSignalCallback &callback);
+
+ private:
+ class Proxy
+ : public org::freedesktop::ModemManager::Modem::Gsm::Network_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ virtual void set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback);
+ virtual void set_network_mode_callback(
+ const NetworkModeSignalCallback &callback);
+ virtual void set_registration_info_callback(
+ const RegistrationInfoSignalCallback &callback);
+
+ private:
+ // Signal callbacks inherited from ModemManager::Modem::Gsm::Network_proxy.
+ virtual void SignalQuality(const uint32_t &quality);
+ virtual void RegistrationInfo(const uint32_t &status,
+ const std::string &operator_code,
+ const std::string &operator_name);
+ virtual void NetworkMode(const uint32_t &mode);
+
+ // Method callbacks inherited from ModemManager::Modem::Gsm::Network_proxy.
+ virtual void RegisterCallback(const DBus::Error &dberror, void *data);
+ virtual void GetRegistrationInfoCallback(const GSMRegistrationInfo &info,
+ const DBus::Error &dberror,
+ void *data);
+ virtual void GetSignalQualityCallback(const uint32_t &quality,
+ const DBus::Error &dberror,
+ void *data);
+ virtual void ScanCallback(const GSMScanResults &results,
+ const DBus::Error &dberror, void *data);
+
+ SignalQualitySignalCallback signal_quality_callback_;
+ RegistrationInfoSignalCallback registration_info_callback_;
+ NetworkModeSignalCallback network_mode_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemGSMNetworkProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_GSM_NETWORK_PROXY_H_
diff --git a/cellular/modem_gsm_network_proxy_interface.h b/cellular/modem_gsm_network_proxy_interface.h
new file mode 100644
index 0000000..740e6d4
--- /dev/null
+++ b/cellular/modem_gsm_network_proxy_interface.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_GSM_NETWORK_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MODEM_GSM_NETWORK_PROXY_INTERFACE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <dbus-c++/types.h>
+
+#include "shill/callbacks.h"
+
+namespace shill {
+
+class Error;
+
+typedef DBus::Struct<unsigned int, std::string, std::string>
+ GSMRegistrationInfo;
+typedef std::map<std::string, std::string> GSMScanResult;
+typedef std::vector<GSMScanResult> GSMScanResults;
+
+typedef base::Callback<void(uint32_t)> SignalQualitySignalCallback;
+typedef base::Callback<void(
+ uint32_t,
+ const std::string &,
+ const std::string &)> RegistrationInfoSignalCallback;
+typedef base::Callback<void(uint32_t)> NetworkModeSignalCallback;
+
+typedef base::Callback<void(uint32_t, const Error &)> SignalQualityCallback;
+typedef base::Callback<void(uint32_t,
+ const std::string &,
+ const std::string &,
+ const Error &)> RegistrationInfoCallback;
+typedef base::Callback<void(const GSMScanResults &,
+ const Error &)> ScanResultsCallback;
+
+// These are the methods that a ModemManager.Modem.Gsm.Network proxy must
+// support. The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously.
+// XXX fixup comment to reflect new reality
+class ModemGSMNetworkProxyInterface {
+ public:
+ virtual ~ModemGSMNetworkProxyInterface() {}
+
+ virtual void GetRegistrationInfo(Error *error,
+ const RegistrationInfoCallback &callback,
+ int timeout) = 0;
+ virtual void GetSignalQuality(Error *error,
+ const SignalQualityCallback &callback,
+ int timeout) = 0;
+ virtual void Register(const std::string &network_id,
+ Error *error, const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void Scan(Error *error, const ScanResultsCallback &callback,
+ int timeout) = 0;
+
+ // Properties.
+ virtual uint32_t AccessTechnology() = 0;
+ // Signal callbacks
+ virtual void set_signal_quality_callback(
+ const SignalQualitySignalCallback &callback) = 0;
+ virtual void set_network_mode_callback(
+ const NetworkModeSignalCallback &callback) = 0;
+ virtual void set_registration_info_callback(
+ const RegistrationInfoSignalCallback &callback) = 0;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_GSM_NETWORK_PROXY_INTERFACE_H_
diff --git a/cellular/modem_info.cc b/cellular/modem_info.cc
new file mode 100644
index 0000000..04d1455
--- /dev/null
+++ b/cellular/modem_info.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 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_info.h"
+
+#include <base/files/file_path.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/cellular/modem_manager.h"
+#include "shill/logging.h"
+#include "shill/manager.h"
+#include "shill/pending_activation_store.h"
+
+using base::FilePath;
+using std::string;
+
+namespace shill {
+
+ModemInfo::ModemInfo(ControlInterface *control_interface,
+ EventDispatcher *dispatcher,
+ Metrics *metrics,
+ Manager *manager,
+ GLib *glib)
+ : control_interface_(control_interface),
+ dispatcher_(dispatcher),
+ metrics_(metrics),
+ manager_(manager),
+ glib_(glib) {}
+
+ModemInfo::~ModemInfo() {
+ Stop();
+}
+
+void ModemInfo::Start() {
+ pending_activation_store_.reset(new PendingActivationStore());
+ pending_activation_store_->InitStorage(manager_->glib(),
+ manager_->storage_path());
+
+ RegisterModemManager(new ModemManagerClassic(cromo::kCromoServiceName,
+ cromo::kCromoServicePath,
+ this));
+ RegisterModemManager(
+ new ModemManager1(modemmanager::kModemManager1ServiceName,
+ modemmanager::kModemManager1ServicePath,
+ this));
+}
+
+void ModemInfo::Stop() {
+ pending_activation_store_.reset();
+ modem_managers_.clear();
+}
+
+void ModemInfo::OnDeviceInfoAvailable(const string &link_name) {
+ for (const auto &manager : modem_managers_) {
+ manager->OnDeviceInfoAvailable(link_name);
+ }
+}
+
+void ModemInfo::set_pending_activation_store(
+ PendingActivationStore *pending_activation_store) {
+ pending_activation_store_.reset(pending_activation_store);
+}
+
+void ModemInfo::RegisterModemManager(ModemManager *manager) {
+ modem_managers_.push_back(manager); // Passes ownership.
+ manager->Start();
+}
+
+} // namespace shill
diff --git a/cellular/modem_info.h b/cellular/modem_info.h
new file mode 100644
index 0000000..7b886ab
--- /dev/null
+++ b/cellular/modem_info.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_INFO_H_
+#define SHILL_CELLULAR_MODEM_INFO_H_
+
+#include <memory>
+#include <string>
+
+#include <base/memory/scoped_vector.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+namespace shill {
+
+class ControlInterface;
+class EventDispatcher;
+class GLib;
+class Manager;
+class Metrics;
+class ModemManager;
+class PendingActivationStore;
+
+// Manages modem managers.
+class ModemInfo {
+ public:
+ ModemInfo(ControlInterface *control,
+ EventDispatcher *dispatcher,
+ Metrics *metrics,
+ Manager *manager,
+ GLib *glib);
+ virtual ~ModemInfo();
+
+ virtual void Start();
+ virtual void Stop();
+
+ virtual void OnDeviceInfoAvailable(const std::string &link_name);
+
+ ControlInterface *control_interface() const { return control_interface_; }
+ EventDispatcher *dispatcher() const { return dispatcher_; }
+ Metrics *metrics() const { return metrics_; }
+ Manager *manager() const { return manager_; }
+ GLib *glib() const { return glib_; }
+ PendingActivationStore *pending_activation_store() const {
+ return pending_activation_store_.get();
+ }
+
+ protected:
+ // Write accessors for unit-tests.
+ void set_control_interface(ControlInterface *control) {
+ control_interface_ = control;
+ }
+ void set_event_dispatcher(EventDispatcher *dispatcher) {
+ dispatcher_ = dispatcher;
+ }
+ void set_metrics(Metrics *metrics) {
+ metrics_ = metrics;
+ }
+ void set_manager(Manager *manager) {
+ manager_ = manager;
+ }
+ void set_glib(GLib *glib) {
+ glib_ = glib;
+ }
+ void set_pending_activation_store(
+ PendingActivationStore *pending_activation_store);
+
+ private:
+ friend class ModemInfoTest;
+ FRIEND_TEST(ModemInfoTest, RegisterModemManager);
+ FRIEND_TEST(ModemInfoTest, StartStop);
+
+ typedef ScopedVector<ModemManager> ModemManagers;
+
+ // Registers and starts |manager|. Takes ownership of |manager|.
+ void RegisterModemManager(ModemManager *manager);
+ ModemManagers modem_managers_;
+
+ ControlInterface *control_interface_;
+ EventDispatcher *dispatcher_;
+ Metrics *metrics_;
+ Manager *manager_;
+ GLib *glib_;
+
+ // Post-payment activation state of the modem.
+ std::unique_ptr<PendingActivationStore> pending_activation_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemInfo);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_INFO_H_
diff --git a/cellular/modem_info_unittest.cc b/cellular/modem_info_unittest.cc
new file mode 100644
index 0000000..8224919
--- /dev/null
+++ b/cellular/modem_info_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 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_info.h"
+
+#include <base/stl_util.h>
+#include <gtest/gtest.h>
+
+#include "shill/cellular/modem_manager.h"
+#include "shill/manager.h"
+#include "shill/mock_control.h"
+#include "shill/mock_dbus_service_proxy.h"
+#include "shill/mock_glib.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_metrics.h"
+
+using testing::_;
+using testing::Return;
+using testing::Test;
+
+namespace shill {
+
+class ModemInfoTest : public Test {
+ public:
+ ModemInfoTest()
+ : metrics_(&dispatcher_),
+ manager_(&control_interface_, &dispatcher_, &metrics_, &glib_),
+ dbus_service_proxy_(nullptr),
+ modem_info_(&control_interface_, &dispatcher_, &metrics_, &manager_,
+ &glib_) {}
+
+ virtual void SetUp() {
+ manager_.dbus_manager_.reset(new DBusManager());
+ dbus_service_proxy_ = new MockDBusServiceProxy();
+ // Ownership of |dbus_service_proxy_| is transferred to
+ // |manager_.dbus_manager_|.
+ manager_.dbus_manager_->proxy_.reset(dbus_service_proxy_);
+ }
+
+ protected:
+ MockGLib glib_;
+ MockControl control_interface_;
+ EventDispatcher dispatcher_;
+ MockMetrics metrics_;
+ MockManager manager_;
+ MockDBusServiceProxy *dbus_service_proxy_;
+ ModemInfo modem_info_;
+};
+
+TEST_F(ModemInfoTest, StartStop) {
+ EXPECT_EQ(0, modem_info_.modem_managers_.size());
+ EXPECT_CALL(*dbus_service_proxy_,
+ GetNameOwner("org.chromium.ModemManager", _, _, _));
+ EXPECT_CALL(*dbus_service_proxy_,
+ GetNameOwner("org.freedesktop.ModemManager1", _, _, _));
+ modem_info_.Start();
+ EXPECT_EQ(2, modem_info_.modem_managers_.size());
+ modem_info_.Stop();
+ EXPECT_EQ(0, modem_info_.modem_managers_.size());
+}
+
+TEST_F(ModemInfoTest, RegisterModemManager) {
+ static const char kService[] = "some.dbus.service";
+ EXPECT_CALL(*dbus_service_proxy_, GetNameOwner(kService, _, _, _));
+ modem_info_.RegisterModemManager(
+ new ModemManagerClassic(kService, "/dbus/service/path", &modem_info_));
+ ASSERT_EQ(1, modem_info_.modem_managers_.size());
+ ModemManager *manager = modem_info_.modem_managers_[0];
+ EXPECT_EQ(kService, manager->service_);
+ EXPECT_EQ(&modem_info_, manager->modem_info_);
+}
+
+} // namespace shill
diff --git a/cellular/modem_manager.cc b/cellular/modem_manager.cc
new file mode 100644
index 0000000..1d5ca30
--- /dev/null
+++ b/cellular/modem_manager.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2012 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_manager.h"
+
+#include <base/stl_util.h>
+#include <mm/mm-modem.h>
+
+#include "shill/cellular/modem.h"
+#include "shill/cellular/modem_manager_proxy.h"
+#include "shill/dbus_manager.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+#include "shill/manager.h"
+#include "shill/proxy_factory.h"
+
+using std::string;
+using std::shared_ptr;
+using std::vector;
+
+namespace shill {
+
+ModemManager::ModemManager(const string &service,
+ const string &path,
+ ModemInfo *modem_info)
+ : proxy_factory_(ProxyFactory::GetInstance()),
+ service_(service),
+ path_(path),
+ modem_info_(modem_info) {}
+
+ModemManager::~ModemManager() {
+ Stop();
+}
+
+void ModemManager::Start() {
+ LOG(INFO) << "Start watching modem manager service: " << service_;
+ CHECK(!name_watcher_);
+ name_watcher_.reset(modem_info_->manager()->dbus_manager()->CreateNameWatcher(
+ service_,
+ base::Bind(&ModemManager::OnAppear, base::Unretained(this)),
+ base::Bind(&ModemManager::OnVanish, base::Unretained(this))));
+}
+
+void ModemManager::Stop() {
+ LOG(INFO) << "Stop watching modem manager service: " << service_;
+ name_watcher_.reset();
+ Disconnect();
+}
+
+void ModemManager::Connect(const string &owner) {
+ // Inheriting classes call this superclass method.
+ owner_ = owner;
+}
+
+void ModemManager::Disconnect() {
+ // Inheriting classes call this superclass method.
+ modems_.clear();
+ owner_.clear();
+}
+
+void ModemManager::OnAppear(const string &name, const string &owner) {
+ LOG(INFO) << "Modem manager " << name << " appeared. Owner: " << owner;
+ Connect(owner);
+}
+
+void ModemManager::OnVanish(const string &name) {
+ LOG(INFO) << "Modem manager " << name << " vanished.";
+ Disconnect();
+}
+
+bool ModemManager::ModemExists(const std::string &path) const {
+ CHECK(!owner_.empty());
+ if (ContainsKey(modems_, path)) {
+ LOG(INFO) << "ModemExists: " << path << " already exists.";
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ModemManager::RecordAddedModem(shared_ptr<Modem> modem) {
+ modems_[modem->path()] = modem;
+}
+
+void ModemManager::RemoveModem(const string &path) {
+ LOG(INFO) << "Remove modem: " << path;
+ CHECK(!owner_.empty());
+ modems_.erase(path);
+}
+
+void ModemManager::OnDeviceInfoAvailable(const string &link_name) {
+ for (Modems::const_iterator it = modems_.begin(); it != modems_.end(); ++it) {
+ it->second->OnDeviceInfoAvailable(link_name);
+ }
+}
+
+// ModemManagerClassic
+ModemManagerClassic::ModemManagerClassic(
+ const string &service,
+ const string &path,
+ ModemInfo *modem_info)
+ : ModemManager(service,
+ path,
+ modem_info) {}
+
+ModemManagerClassic::~ModemManagerClassic() {}
+
+void ModemManagerClassic::Connect(const string &supplied_owner) {
+ ModemManager::Connect(supplied_owner);
+ proxy_.reset(proxy_factory()->CreateModemManagerProxy(this, path(), owner()));
+ // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
+ vector<DBus::Path> devices = proxy_->EnumerateDevices();
+
+ for (vector<DBus::Path>::const_iterator it = devices.begin();
+ it != devices.end(); ++it) {
+ AddModemClassic(*it);
+ }
+}
+
+void ModemManagerClassic::AddModemClassic(const string &path) {
+ if (ModemExists(path)) {
+ return;
+ }
+ shared_ptr<ModemClassic> modem(new ModemClassic(owner(),
+ service(),
+ path,
+ modem_info()));
+ RecordAddedModem(modem);
+ InitModemClassic(modem);
+}
+
+void ModemManagerClassic::Disconnect() {
+ ModemManager::Disconnect();
+ proxy_.reset();
+}
+
+void ModemManagerClassic::InitModemClassic(shared_ptr<ModemClassic> modem) {
+ // TODO(rochberg): Switch to asynchronous calls (crbug.com/200687).
+ if (modem == nullptr) {
+ return;
+ }
+
+ std::unique_ptr<DBusPropertiesProxyInterface> properties_proxy(
+ proxy_factory()->CreateDBusPropertiesProxy(modem->path(),
+ modem->owner()));
+ DBusPropertiesMap properties =
+ properties_proxy->GetAll(MM_MODEM_INTERFACE);
+
+ modem->CreateDeviceClassic(properties);
+}
+
+void ModemManagerClassic::OnDeviceAdded(const string &path) {
+ AddModemClassic(path);
+}
+
+void ModemManagerClassic::OnDeviceRemoved(const string &path) {
+ RemoveModem(path);
+}
+
+} // namespace shill
diff --git a/cellular/modem_manager.h b/cellular/modem_manager.h
new file mode 100644
index 0000000..cd43df5
--- /dev/null
+++ b/cellular/modem_manager.h
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_MANAGER_H_
+#define SHILL_CELLULAR_MODEM_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/cellular/dbus_objectmanager_proxy_interface.h"
+#include "shill/cellular/modem_info.h"
+#include "shill/dbus_properties_proxy_interface.h"
+#include "shill/glib.h"
+
+namespace shill {
+
+class DBusNameWatcher;
+class DBusObjectManagerProxyInterface;
+class DBusPropertiesProxyInterface;
+class Modem1;
+class Modem;
+class ModemClassic;
+class ModemManagerProxyInterface;
+class ProxyFactory;
+
+// Handles a modem manager service and creates and destroys modem instances.
+class ModemManager {
+ public:
+ ModemManager(const std::string &service,
+ const std::string &path,
+ ModemInfo *modem_info);
+ virtual ~ModemManager();
+
+ // Starts watching for and handling the DBus modem manager service.
+ void Start();
+
+ // Stops watching for the DBus modem manager service and destroys any
+ // associated modems.
+ void Stop();
+
+ // DBusNameWatcher callbacks.
+ void OnAppear(const std::string &name, const std::string &owner);
+ void OnVanish(const std::string &name);
+
+ void OnDeviceInfoAvailable(const std::string &link_name);
+
+ protected:
+ typedef std::map<std::string, std::shared_ptr<Modem>> Modems;
+
+ const std::string &owner() const { return owner_; }
+ const std::string &service() const { return service_; }
+ const std::string &path() const { return path_; }
+ ProxyFactory *proxy_factory() const { return proxy_factory_; }
+ ModemInfo *modem_info() const { return modem_info_; }
+
+ // Connect/Disconnect to a modem manager service.
+ // Inheriting classes must call this superclass method.
+ virtual void Connect(const std::string &owner);
+ // Inheriting classes must call this superclass method.
+ virtual void Disconnect();
+
+ bool ModemExists(const std::string &path) const;
+ // Put the modem into our modem map
+ void RecordAddedModem(std::shared_ptr<Modem> modem);
+
+ // Removes a modem on |path|.
+ void RemoveModem(const std::string &path);
+
+ private:
+ friend class ModemManagerCoreTest;
+ friend class ModemManagerClassicTest;
+ friend class ModemManager1Test;
+
+ FRIEND_TEST(ModemInfoTest, RegisterModemManager);
+ FRIEND_TEST(ModemManager1Test, AddRemoveInterfaces);
+ FRIEND_TEST(ModemManager1Test, Connect);
+ FRIEND_TEST(ModemManagerClassicTest, Connect);
+ FRIEND_TEST(ModemManagerCoreTest, AddRemoveModem);
+ FRIEND_TEST(ModemManagerCoreTest, ConnectDisconnect);
+ FRIEND_TEST(ModemManagerCoreTest, OnAppearVanish);
+ FRIEND_TEST(ModemManagerCoreTest, StartStopWithModemManagerServiceAbsent);
+ FRIEND_TEST(ModemManagerCoreTest, StartStopWithModemManagerServicePresent);
+
+ // Store cached copies of singletons for speed/ease of testing.
+ ProxyFactory *proxy_factory_;
+
+ const std::string service_;
+ const std::string path_;
+ std::unique_ptr<DBusNameWatcher> name_watcher_;
+
+ std::string owner_; // DBus service owner.
+
+ Modems modems_; // Maps a modem |path| to a modem instance.
+
+ ModemInfo *modem_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemManager);
+};
+
+class ModemManagerClassic : public ModemManager {
+ public:
+ ModemManagerClassic(const std::string &service,
+ const std::string &path,
+ ModemInfo *modem_info);
+
+ ~ModemManagerClassic() override;
+
+ // Called by our dbus proxy
+ void OnDeviceAdded(const std::string &path);
+ void OnDeviceRemoved(const std::string &path);
+
+ protected:
+ virtual void Connect(const std::string &owner);
+ virtual void Disconnect();
+
+ virtual void AddModemClassic(const std::string &path);
+ virtual void InitModemClassic(std::shared_ptr<ModemClassic> modem);
+
+ private:
+ std::unique_ptr<ModemManagerProxyInterface> proxy_; // DBus service proxy
+ std::unique_ptr<DBusPropertiesProxyInterface> dbus_properties_proxy_;
+
+ FRIEND_TEST(ModemManagerClassicTest, Connect);
+
+ DISALLOW_COPY_AND_ASSIGN(ModemManagerClassic);
+};
+
+class ModemManager1 : public ModemManager {
+ public:
+ ModemManager1(const std::string &service,
+ const std::string &path,
+ ModemInfo *modem_info);
+
+ ~ModemManager1() override;
+
+ protected:
+ void AddModem1(const std::string &path,
+ const DBusInterfaceToProperties &properties);
+ virtual void InitModem1(std::shared_ptr<Modem1> modem,
+ const DBusInterfaceToProperties &properties);
+
+ // ModemManager methods
+ virtual void Connect(const std::string &owner);
+ virtual void Disconnect();
+
+ // DBusObjectManagerProxyDelegate signal methods
+ virtual void OnInterfacesAddedSignal(
+ const ::DBus::Path &object_path,
+ const DBusInterfaceToProperties &properties);
+ virtual void OnInterfacesRemovedSignal(
+ const ::DBus::Path &object_path,
+ const std::vector<std::string> &interfaces);
+
+ // DBusObjectManagerProxyDelegate method callbacks
+ virtual void OnGetManagedObjectsReply(
+ const DBusObjectsWithProperties &objects_with_properties,
+ const Error &error);
+
+ private:
+ friend class ModemManager1Test;
+ FRIEND_TEST(ModemManager1Test, Connect);
+ FRIEND_TEST(ModemManager1Test, AddRemoveInterfaces);
+
+ std::unique_ptr<DBusObjectManagerProxyInterface> proxy_;
+ base::WeakPtrFactory<ModemManager1> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemManager1);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_MANAGER_H_
diff --git a/cellular/modem_manager_1.cc b/cellular/modem_manager_1.cc
new file mode 100644
index 0000000..9caab76
--- /dev/null
+++ b/cellular/modem_manager_1.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 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_manager.h"
+
+#include <base/bind.h>
+#include <base/stl_util.h>
+#include <ModemManager/ModemManager.h>
+
+#include "shill/cellular/modem.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+#include "shill/proxy_factory.h"
+
+using base::Bind;
+using std::string;
+using std::shared_ptr;
+using std::vector;
+
+namespace shill {
+
+ModemManager1::ModemManager1(const string &service,
+ const string &path,
+ ModemInfo *modem_info)
+ : ModemManager(service,
+ path,
+ modem_info),
+ weak_ptr_factory_(this) {}
+
+ModemManager1::~ModemManager1() {}
+
+void ModemManager1::Connect(const string &supplied_owner) {
+ ModemManager::Connect(supplied_owner);
+ proxy_.reset(
+ proxy_factory()->CreateDBusObjectManagerProxy(path(), owner()));
+ proxy_->set_interfaces_added_callback(
+ Bind(&ModemManager1::OnInterfacesAddedSignal,
+ weak_ptr_factory_.GetWeakPtr()));
+ proxy_->set_interfaces_removed_callback(
+ Bind(&ModemManager1::OnInterfacesRemovedSignal,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ // TODO(rochberg): Make global kDBusDefaultTimeout and use it here
+ Error error;
+ proxy_->GetManagedObjects(&error,
+ Bind(&ModemManager1::OnGetManagedObjectsReply,
+ weak_ptr_factory_.GetWeakPtr()),
+ 5000);
+}
+
+void ModemManager1::Disconnect() {
+ ModemManager::Disconnect();
+ proxy_.reset();
+}
+
+void ModemManager1::AddModem1(const string &path,
+ const DBusInterfaceToProperties &properties) {
+ if (ModemExists(path)) {
+ return;
+ }
+ shared_ptr<Modem1> modem1(new Modem1(owner(),
+ service(),
+ path,
+ modem_info()));
+ RecordAddedModem(modem1);
+ InitModem1(modem1, properties);
+}
+
+void ModemManager1::InitModem1(shared_ptr<Modem1> modem,
+ const DBusInterfaceToProperties &properties) {
+ if (modem == nullptr) {
+ return;
+ }
+ modem->CreateDeviceMM1(properties);
+}
+
+// signal methods
+// Also called by OnGetManagedObjectsReply
+void ModemManager1::OnInterfacesAddedSignal(
+ const ::DBus::Path &object_path,
+ const DBusInterfaceToProperties &properties) {
+ if (ContainsKey(properties, MM_DBUS_INTERFACE_MODEM)) {
+ AddModem1(object_path, properties);
+ } else {
+ LOG(ERROR) << "Interfaces added, but not modem interface.";
+ }
+}
+
+void ModemManager1::OnInterfacesRemovedSignal(
+ const ::DBus::Path &object_path,
+ const vector<string> &interfaces) {
+ LOG(INFO) << "MM1: Removing interfaces from " << object_path;
+ if (find(interfaces.begin(),
+ interfaces.end(),
+ MM_DBUS_INTERFACE_MODEM) != interfaces.end()) {
+ RemoveModem(object_path);
+ } else {
+ // In theory, a modem could drop, say, 3GPP, but not CDMA. In
+ // practice, we don't expect this
+ LOG(ERROR) << "Interfaces removed, but not modem interface";
+ }
+}
+
+// DBusObjectManagerProxy async method call
+void ModemManager1::OnGetManagedObjectsReply(
+ const DBusObjectsWithProperties &objects,
+ const Error &error) {
+ if (error.IsSuccess()) {
+ DBusObjectsWithProperties::const_iterator m;
+ for (m = objects.begin(); m != objects.end(); ++m) {
+ OnInterfacesAddedSignal(m->first, m->second);
+ }
+ }
+}
+
+} // namespace shill
diff --git a/cellular/modem_manager_proxy.cc b/cellular/modem_manager_proxy.cc
new file mode 100644
index 0000000..cc017a6
--- /dev/null
+++ b/cellular/modem_manager_proxy.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 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_manager_proxy.h"
+
+#include "shill/cellular/modem_manager.h"
+#include "shill/logging.h"
+
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kDBus;
+static string ObjectID(const DBus::Path *p) { return *p; }
+}
+
+ModemManagerProxy::ModemManagerProxy(DBus::Connection *connection,
+ ModemManagerClassic *manager,
+ const string &path,
+ const string &service)
+ : proxy_(connection, manager, path, service) {}
+
+ModemManagerProxy::~ModemManagerProxy() {}
+
+vector<DBus::Path> ModemManagerProxy::EnumerateDevices() {
+ SLOG(&proxy_.path(), 2) << __func__;
+ try {
+ return proxy_.EnumerateDevices();
+ } catch (const DBus::Error &e) {
+ LOG(ERROR) << "DBus exception: " << e.name() << ": " << e.what();
+ }
+ return vector<DBus::Path>();
+}
+
+ModemManagerProxy::Proxy::Proxy(DBus::Connection *connection,
+ ModemManagerClassic *manager,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()),
+ manager_(manager) {}
+
+ModemManagerProxy::Proxy::~Proxy() {}
+
+void ModemManagerProxy::Proxy::DeviceAdded(const DBus::Path &device) {
+ SLOG(&path(), 2) << __func__;
+ manager_->OnDeviceAdded(device);
+}
+
+void ModemManagerProxy::Proxy::DeviceRemoved(const DBus::Path &device) {
+ SLOG(&path(), 2) << __func__;
+ manager_->OnDeviceRemoved(device);
+}
+
+} // namespace shill
diff --git a/cellular/modem_manager_proxy.h b/cellular/modem_manager_proxy.h
new file mode 100644
index 0000000..a8b850c
--- /dev/null
+++ b/cellular/modem_manager_proxy.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_MANAGER_PROXY_H_
+#define SHILL_CELLULAR_MODEM_MANAGER_PROXY_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "dbus_proxies/org.freedesktop.ModemManager.h"
+#include "shill/cellular/modem_manager_proxy_interface.h"
+
+namespace shill {
+
+class ModemManagerClassic;
+
+// There's a single proxy per (old) ModemManager service identified by
+// its DBus |path| and owner name |service|.
+class ModemManagerProxy : public ModemManagerProxyInterface {
+ public:
+ ModemManagerProxy(DBus::Connection *connection,
+ ModemManagerClassic *manager,
+ const std::string &path,
+ const std::string &service);
+ ~ModemManagerProxy() override;
+
+ // Inherited from ModemManagerProxyInterface.
+ virtual std::vector<DBus::Path> EnumerateDevices();
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ ModemManagerClassic *manager,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Signal callbacks inherited from ModemManager_proxy.
+ virtual void DeviceAdded(const DBus::Path &device);
+ virtual void DeviceRemoved(const DBus::Path &device);
+
+ // Method callbacks inherited from ModemManager_proxy.
+ // [None]
+
+ // The owner of this proxy.
+ ModemManagerClassic *manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemManagerProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_MANAGER_PROXY_H_
diff --git a/cellular/modem_manager_proxy_interface.h b/cellular/modem_manager_proxy_interface.h
new file mode 100644
index 0000000..a5eba33
--- /dev/null
+++ b/cellular/modem_manager_proxy_interface.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 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.
+
+#ifndef SHILL_CELLULAR_MODEM_MANAGER_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MODEM_MANAGER_PROXY_INTERFACE_H_
+
+#include <vector>
+
+#include <dbus-c++/types.h>
+
+namespace shill {
+
+// These are the methods that a ModemManager proxy must support. The interface
+// is provided so that it can be mocked in tests.
+class ModemManagerProxyInterface {
+ public:
+ virtual ~ModemManagerProxyInterface() {}
+
+ virtual std::vector<DBus::Path> EnumerateDevices() = 0;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_MANAGER_PROXY_INTERFACE_H_
diff --git a/cellular/modem_manager_unittest.cc b/cellular/modem_manager_unittest.cc
new file mode 100644
index 0000000..ececf60
--- /dev/null
+++ b/cellular/modem_manager_unittest.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2012 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_manager.h"
+
+#include <base/stl_util.h>
+#include <ModemManager/ModemManager.h>
+
+#include "shill/cellular/mock_dbus_objectmanager_proxy.h"
+#include "shill/cellular/mock_modem.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/cellular/mock_modem_manager_proxy.h"
+#include "shill/manager.h"
+#include "shill/mock_control.h"
+#include "shill/mock_dbus_service_proxy.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_proxy_factory.h"
+#include "shill/testing.h"
+
+using std::string;
+using std::shared_ptr;
+using std::vector;
+using testing::_;
+using testing::Invoke;
+using testing::Pointee;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrEq;
+using testing::Test;
+
+namespace shill {
+
+class ModemManagerTest : public Test {
+ public:
+ ModemManagerTest()
+ : manager_(&control_, &dispatcher_, nullptr, nullptr),
+ modem_info_(&control_, &dispatcher_, nullptr, &manager_, nullptr),
+ dbus_service_proxy_(nullptr) {}
+
+ virtual void SetUp() {
+ modem_.reset(new StrictModem(kOwner, kService, kModemPath, &modem_info_));
+ manager_.dbus_manager_.reset(new DBusManager());
+ dbus_service_proxy_ = new MockDBusServiceProxy();
+ // Ownership of |dbus_service_proxy_| is transferred to
+ // |manager_.dbus_manager_|.
+ manager_.dbus_manager_->proxy_.reset(dbus_service_proxy_);
+ }
+
+ protected:
+ static const char kService[];
+ static const char kPath[];
+ static const char kOwner[];
+ static const char kModemPath[];
+
+ shared_ptr<StrictModem> modem_;
+
+ EventDispatcher dispatcher_;
+ MockControl control_;
+ MockManager manager_;
+ MockModemInfo modem_info_;
+ MockProxyFactory proxy_factory_;
+ MockDBusServiceProxy *dbus_service_proxy_;
+};
+
+const char ModemManagerTest::kService[] = "org.chromium.ModemManager";
+const char ModemManagerTest::kPath[] = "/org/chromium/ModemManager";
+const char ModemManagerTest::kOwner[] = ":1.17";
+const char ModemManagerTest::kModemPath[] = "/org/blah/Modem/blah/0";
+
+class ModemManagerCoreTest : public ModemManagerTest {
+ public:
+ ModemManagerCoreTest()
+ : ModemManagerTest(),
+ modem_manager_(kService, kPath, &modem_info_) {}
+
+ protected:
+ ModemManager modem_manager_;
+};
+
+TEST_F(ModemManagerCoreTest, StartStopWithModemManagerServiceAbsent) {
+ StringCallback get_name_owner_callback;
+ EXPECT_CALL(*dbus_service_proxy_, GetNameOwner(kService, _, _, _))
+ .WillOnce(SaveArg<2>(&get_name_owner_callback));
+ modem_manager_.Start();
+ get_name_owner_callback.Run("", Error());
+ EXPECT_EQ("", modem_manager_.owner_);
+
+ modem_manager_.Stop();
+ EXPECT_EQ("", modem_manager_.owner_);
+}
+
+TEST_F(ModemManagerCoreTest, StartStopWithModemManagerServicePresent) {
+ StringCallback get_name_owner_callback;
+ EXPECT_CALL(*dbus_service_proxy_, GetNameOwner(kService, _, _, _))
+ .WillOnce(SaveArg<2>(&get_name_owner_callback));
+ modem_manager_.Start();
+ get_name_owner_callback.Run(kOwner, Error());
+ EXPECT_EQ(kOwner, modem_manager_.owner_);
+
+ modem_manager_.Stop();
+ EXPECT_EQ("", modem_manager_.owner_);
+}
+
+TEST_F(ModemManagerCoreTest, OnAppearVanish) {
+ EXPECT_CALL(*dbus_service_proxy_, GetNameOwner(kService, _, _, _));
+ modem_manager_.Start();
+ EXPECT_EQ("", modem_manager_.owner_);
+
+ manager_.dbus_manager()->OnNameOwnerChanged(kService, "", kOwner);
+ EXPECT_EQ(kOwner, modem_manager_.owner_);
+
+ manager_.dbus_manager()->OnNameOwnerChanged(kService, kOwner, "");
+ EXPECT_EQ("", modem_manager_.owner_);
+}
+
+TEST_F(ModemManagerCoreTest, ConnectDisconnect) {
+ EXPECT_EQ("", modem_manager_.owner_);
+ modem_manager_.Connect(kOwner);
+ EXPECT_EQ(kOwner, modem_manager_.owner_);
+ EXPECT_EQ(0, modem_manager_.modems_.size());
+
+ modem_manager_.RecordAddedModem(modem_);
+ EXPECT_EQ(1, modem_manager_.modems_.size());
+
+ modem_manager_.ModemManager::Disconnect();
+ EXPECT_EQ("", modem_manager_.owner_);
+ EXPECT_EQ(0, modem_manager_.modems_.size());
+}
+
+TEST_F(ModemManagerCoreTest, AddRemoveModem) {
+ modem_manager_.Connect(kOwner);
+ EXPECT_FALSE(modem_manager_.ModemExists(kModemPath));
+
+ // Remove non-existent modem path.
+ modem_manager_.RemoveModem(kModemPath);
+ EXPECT_FALSE(modem_manager_.ModemExists(kModemPath));
+
+ modem_manager_.RecordAddedModem(modem_);
+ EXPECT_TRUE(modem_manager_.ModemExists(kModemPath));
+
+ // Add an already added modem.
+ modem_manager_.RecordAddedModem(modem_);
+ EXPECT_TRUE(modem_manager_.ModemExists(kModemPath));
+
+ modem_manager_.RemoveModem(kModemPath);
+ EXPECT_FALSE(modem_manager_.ModemExists(kModemPath));
+
+ // Remove an already removed modem path.
+ modem_manager_.RemoveModem(kModemPath);
+ EXPECT_FALSE(modem_manager_.ModemExists(kModemPath));
+}
+
+class ModemManagerClassicMockInit : public ModemManagerClassic {
+ public:
+ ModemManagerClassicMockInit(const string &service,
+ const string &path,
+ ModemInfo *modem_info_) :
+ ModemManagerClassic(service, path, modem_info_) {}
+
+ MOCK_METHOD1(InitModemClassic, void(shared_ptr<ModemClassic>));
+};
+
+class ModemManagerClassicTest : public ModemManagerTest {
+ public:
+ ModemManagerClassicTest()
+ : ModemManagerTest(),
+ modem_manager_(kService, kPath, &modem_info_),
+ proxy_(new MockModemManagerProxy()) {}
+
+ protected:
+ virtual void SetUp() {
+ modem_manager_.proxy_factory_ = &proxy_factory_;
+ }
+
+ virtual void TearDown() {
+ modem_manager_.proxy_factory_ = nullptr;
+ }
+
+ ModemManagerClassicMockInit modem_manager_;
+ std::unique_ptr<MockModemManagerProxy> proxy_;
+};
+
+TEST_F(ModemManagerClassicTest, Connect) {
+ EXPECT_EQ("", modem_manager_.owner_);
+
+ EXPECT_CALL(proxy_factory_, CreateModemManagerProxy(_, kPath, kOwner))
+ .WillOnce(ReturnAndReleasePointee(&proxy_));
+ EXPECT_CALL(*proxy_, EnumerateDevices())
+ .WillOnce(Return(vector<DBus::Path>(1, kModemPath)));
+
+ EXPECT_CALL(modem_manager_,
+ InitModemClassic(
+ Pointee(Field(&Modem::path_, StrEq(kModemPath)))));
+
+ modem_manager_.Connect(kOwner);
+ EXPECT_EQ(kOwner, modem_manager_.owner_);
+ EXPECT_EQ(1, modem_manager_.modems_.size());
+ ASSERT_TRUE(ContainsKey(modem_manager_.modems_, kModemPath));
+}
+
+
+class ModemManager1MockInit : public ModemManager1 {
+ public:
+ ModemManager1MockInit(const string &service,
+ const string &path,
+ ModemInfo *modem_info_) :
+ ModemManager1(service, path, modem_info_) {}
+ MOCK_METHOD2(InitModem1, void(shared_ptr<Modem1>,
+ const DBusInterfaceToProperties &));
+};
+
+
+class ModemManager1Test : public ModemManagerTest {
+ public:
+ ModemManager1Test()
+ : ModemManagerTest(),
+ modem_manager_(kService, kPath, &modem_info_),
+ proxy_(new MockDBusObjectManagerProxy()) {}
+
+ protected:
+ virtual void SetUp() {
+ modem_manager_.proxy_factory_ = &proxy_factory_;
+ proxy_->IgnoreSetCallbacks();
+ }
+
+ virtual void TearDown() {
+ modem_manager_.proxy_factory_ = nullptr;
+ }
+
+ void Connect(const DBusObjectsWithProperties &expected_objects) {
+ EXPECT_CALL(proxy_factory_, CreateDBusObjectManagerProxy(kPath, kOwner))
+ .WillOnce(ReturnAndReleasePointee(&proxy_));
+ EXPECT_CALL(*proxy_, set_interfaces_added_callback(_));
+ EXPECT_CALL(*proxy_, set_interfaces_removed_callback(_));
+ ManagedObjectsCallback get_managed_objects_callback;
+ EXPECT_CALL(*proxy_, GetManagedObjects(_, _, _))
+ .WillOnce(SaveArg<1>(&get_managed_objects_callback));
+ modem_manager_.Connect(kOwner);
+ get_managed_objects_callback.Run(expected_objects, Error());
+ }
+
+ static DBusObjectsWithProperties GetModemWithProperties() {
+ DBusPropertiesMap o_fd_mm1_modem;
+
+ DBusInterfaceToProperties properties;
+ properties[MM_DBUS_INTERFACE_MODEM] = o_fd_mm1_modem;
+
+ DBusObjectsWithProperties objects_with_properties;
+ objects_with_properties[kModemPath] = properties;
+
+ return objects_with_properties;
+ }
+
+ ModemManager1MockInit modem_manager_;
+ std::unique_ptr<MockDBusObjectManagerProxy> proxy_;
+ MockProxyFactory proxy_factory_;
+};
+
+TEST_F(ModemManager1Test, Connect) {
+ Connect(GetModemWithProperties());
+ EXPECT_EQ(1, modem_manager_.modems_.size());
+ EXPECT_TRUE(ContainsKey(modem_manager_.modems_, kModemPath));
+}
+
+TEST_F(ModemManager1Test, AddRemoveInterfaces) {
+ // Have nothing come back from GetManagedObjects
+ Connect(DBusObjectsWithProperties());
+ EXPECT_EQ(0, modem_manager_.modems_.size());
+
+ // Add an object that doesn't have a modem interface. Nothing should be added
+ EXPECT_CALL(modem_manager_, InitModem1(_, _)).Times(0);
+ modem_manager_.OnInterfacesAddedSignal(kModemPath,
+ DBusInterfaceToProperties());
+ EXPECT_EQ(0, modem_manager_.modems_.size());
+
+ // Actually add a modem
+ EXPECT_CALL(modem_manager_, InitModem1(_, _)).Times(1);
+ modem_manager_.OnInterfacesAddedSignal(kModemPath,
+ GetModemWithProperties()[kModemPath]);
+ EXPECT_EQ(1, modem_manager_.modems_.size());
+
+ // Remove an irrelevant interface
+ vector<string> not_including_modem_interface;
+ not_including_modem_interface.push_back("not.a.modem.interface");
+ modem_manager_.OnInterfacesRemovedSignal(kModemPath,
+ not_including_modem_interface);
+ EXPECT_EQ(1, modem_manager_.modems_.size());
+
+ // Remove the modem
+ vector<string> with_modem_interface;
+ with_modem_interface.push_back(MM_DBUS_INTERFACE_MODEM);
+ modem_manager_.OnInterfacesRemovedSignal(kModemPath, with_modem_interface);
+ EXPECT_EQ(0, modem_manager_.modems_.size());
+}
+
+} // namespace shill
diff --git a/cellular/modem_proxy.cc b/cellular/modem_proxy.cc
new file mode 100644
index 0000000..f2b5695
--- /dev/null
+++ b/cellular/modem_proxy.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 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_proxy.h"
+
+#include <memory>
+
+#include <base/bind.h>
+#include <base/strings/stringprintf.h>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+
+using base::Bind;
+using base::Callback;
+using base::StringPrintf;
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+typedef Callback<void(const ModemHardwareInfo &,
+ const Error &)> ModemInfoCallback;
+
+ModemProxy::ModemProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemProxy::~ModemProxy() {}
+
+void ModemProxy::set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback) {
+ proxy_.set_state_changed_callback(callback);
+}
+
+void ModemProxy::Enable(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) {
+ BeginAsyncDBusCall(StringPrintf("%s(%s)", __func__,
+ enable ? "true" : "false"),
+ proxy_, &Proxy::EnableAsync, callback, error,
+ &CellularError::FromDBusError, timeout, enable);
+}
+
+void ModemProxy::Disconnect(Error *error, const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::DisconnectAsync, callback,
+ error, &CellularError::FromDBusError, timeout);
+}
+
+void ModemProxy::GetModemInfo(Error *error,
+ const ModemInfoCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetInfoAsync, callback,
+ error, &CellularError::FromDBusError, timeout);
+}
+
+ModemProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemProxy::Proxy::~Proxy() {}
+
+void ModemProxy::Proxy::set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback) {
+ state_changed_callback_ = callback;
+}
+
+void ModemProxy::Proxy::StateChanged(
+ const uint32_t &old, const uint32_t &_new, const uint32_t &reason) {
+ SLOG(&path(), 2) << __func__ << "(" << old << ", " << _new << ", "
+ << reason << ")";
+ state_changed_callback_.Run(old, _new, reason);
+}
+
+void ModemProxy::Proxy::EnableCallback(const DBus::Error &dberror, void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(error);
+}
+
+void ModemProxy::Proxy::GetInfoCallback(const ModemHardwareInfo &info,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ModemInfoCallback> callback(
+ reinterpret_cast<ModemInfoCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(info, error);
+}
+
+void ModemProxy::Proxy::DisconnectCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(error);
+}
+
+} // namespace shill
diff --git a/cellular/modem_proxy.h b/cellular/modem_proxy.h
new file mode 100644
index 0000000..909b7d9
--- /dev/null
+++ b/cellular/modem_proxy.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_PROXY_H_
+#define SHILL_CELLULAR_MODEM_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "dbus_proxies/org.freedesktop.ModemManager.Modem.h"
+#include "shill/cellular/modem_proxy_interface.h"
+
+namespace shill {
+
+// A proxy to (old) ModemManager.Modem.
+class ModemProxy : public ModemProxyInterface {
+ public:
+ // Constructs a ModemManager.Modem DBus object proxy at |path| owned by
+ // |service|.
+ ModemProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemProxy() override;
+
+ // Inherited from ModemProxyInterface.
+ virtual void Enable(bool enable, Error *error,
+ const ResultCallback &callback, int timeout);
+ virtual void Disconnect(Error *error, const ResultCallback &callback,
+ int timeout);
+ virtual void GetModemInfo(Error *error, const ModemInfoCallback &callback,
+ int timeout);
+
+ virtual void set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager::Modem_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ void set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback);
+
+ private:
+ // Signal callbacks inherited from ModemManager::Modem_proxy.
+ virtual void StateChanged(
+ const uint32_t &old, const uint32_t &_new, const uint32_t &reason);
+
+ // Method callbacks inherited from ModemManager::Modem_proxy.
+ virtual void EnableCallback(const DBus::Error &dberror, void *data);
+ virtual void GetInfoCallback(const ModemHardwareInfo &info,
+ const DBus::Error &dberror, void *data);
+ virtual void DisconnectCallback(const DBus::Error &dberror, void *data);
+
+ ModemStateChangedSignalCallback state_changed_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_PROXY_H_
diff --git a/cellular/modem_proxy_interface.h b/cellular/modem_proxy_interface.h
new file mode 100644
index 0000000..77891d5
--- /dev/null
+++ b/cellular/modem_proxy_interface.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MODEM_PROXY_INTERFACE_H_
+
+#include <string>
+
+#include <dbus-c++/types.h>
+
+#include "shill/callbacks.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+
+class CallContext;
+class Error;
+
+typedef DBus::Struct<std::string, std::string, std::string> ModemHardwareInfo;
+
+typedef base::Callback<void(uint32_t, uint32_t, uint32_t)>
+ ModemStateChangedSignalCallback;
+typedef base::Callback<void(const ModemHardwareInfo &,
+ const Error &)> ModemInfoCallback;
+
+// These are the methods that a ModemManager.Modem proxy must support. The
+// interface is provided so that it can be mocked in tests. All calls are
+// made asynchronously.
+class ModemProxyInterface {
+ public:
+ virtual ~ModemProxyInterface() {}
+
+ virtual void Enable(bool enable, Error *error,
+ const ResultCallback &callback, int timeout) = 0;
+ virtual void Disconnect(Error *error, const ResultCallback &callback,
+ int timeout) = 0;
+ virtual void GetModemInfo(Error *error, const ModemInfoCallback &callback,
+ int timeout) = 0;
+
+ virtual void set_state_changed_callback(
+ const ModemStateChangedSignalCallback &callback) = 0;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_PROXY_INTERFACE_H_
diff --git a/cellular/modem_simple_proxy.cc b/cellular/modem_simple_proxy.cc
new file mode 100644
index 0000000..01bf849
--- /dev/null
+++ b/cellular/modem_simple_proxy.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 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_simple_proxy.h"
+
+#include <memory>
+
+#include <base/bind.h>
+
+#include "shill/cellular/cellular_error.h"
+#include "shill/dbus_async_call_helper.h"
+#include "shill/error.h"
+#include "shill/logging.h"
+
+using base::Bind;
+using base::Callback;
+using std::string;
+using std::unique_ptr;
+
+namespace shill {
+
+typedef Callback<void(const DBusPropertiesMap &,
+ const Error &)> ModemStatusCallback;
+
+ModemSimpleProxy::ModemSimpleProxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : proxy_(connection, path, service) {}
+
+ModemSimpleProxy::~ModemSimpleProxy() {}
+
+void ModemSimpleProxy::GetModemStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::GetStatusAsync, callback,
+ error, &CellularError::FromDBusError, timeout);
+}
+
+void ModemSimpleProxy::Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout) {
+ BeginAsyncDBusCall(__func__, proxy_, &Proxy::ConnectAsync, callback,
+ error, &CellularError::FromDBusError, timeout,
+ properties);
+}
+
+ModemSimpleProxy::Proxy::Proxy(DBus::Connection *connection,
+ const string &path,
+ const string &service)
+ : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+ModemSimpleProxy::Proxy::~Proxy() {}
+
+void ModemSimpleProxy::Proxy::GetStatusCallback(const DBusPropertiesMap &props,
+ const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<DBusPropertyMapCallback> callback(
+ reinterpret_cast<DBusPropertyMapCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(props, error);
+}
+
+void ModemSimpleProxy::Proxy::ConnectCallback(const DBus::Error &dberror,
+ void *data) {
+ SLOG(&path(), 2) << __func__;
+ unique_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
+ Error error;
+ CellularError::FromDBusError(dberror, &error);
+ callback->Run(error);
+}
+
+} // namespace shill
diff --git a/cellular/modem_simple_proxy.h b/cellular/modem_simple_proxy.h
new file mode 100644
index 0000000..6056a24
--- /dev/null
+++ b/cellular/modem_simple_proxy.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_SIMPLE_PROXY_H_
+#define SHILL_CELLULAR_MODEM_SIMPLE_PROXY_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "dbus_proxies/org.freedesktop.ModemManager.Modem.Simple.h"
+#include "shill/cellular/modem_simple_proxy_interface.h"
+
+namespace shill {
+
+// A proxy to (old) ModemManager.Modem.Simple.
+class ModemSimpleProxy : public ModemSimpleProxyInterface {
+ public:
+ // Constructs a ModemManager.Modem.Simple DBus object proxy at
+ // |path| owned by |service|.
+ ModemSimpleProxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~ModemSimpleProxy() override;
+
+ // Inherited from ModemSimpleProxyInterface.
+ virtual void GetModemStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout);
+ virtual void Connect(const DBusPropertiesMap &properties,
+ Error *error,
+ const ResultCallback &callback,
+ int timeout);
+
+ private:
+ class Proxy : public org::freedesktop::ModemManager::Modem::Simple_proxy,
+ public DBus::ObjectProxy {
+ public:
+ Proxy(DBus::Connection *connection,
+ const std::string &path,
+ const std::string &service);
+ ~Proxy() override;
+
+ private:
+ // Signal callbacks inherited from ModemManager::Modem::Simple_proxy.
+ // [None]
+
+ // Method callbacks inherited from ModemManager::Modem::Simple_proxy.
+ virtual void GetStatusCallback(const DBusPropertiesMap &props,
+ const DBus::Error &dberror, void *data);
+ virtual void ConnectCallback(const DBus::Error &dberror, void *data);
+
+ DISALLOW_COPY_AND_ASSIGN(Proxy);
+ };
+
+ Proxy proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModemSimpleProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_SIMPLE_PROXY_H_
diff --git a/cellular/modem_simple_proxy_interface.h b/cellular/modem_simple_proxy_interface.h
new file mode 100644
index 0000000..6fd35e9
--- /dev/null
+++ b/cellular/modem_simple_proxy_interface.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_CELLULAR_MODEM_SIMPLE_PROXY_INTERFACE_H_
+#define SHILL_CELLULAR_MODEM_SIMPLE_PROXY_INTERFACE_H_
+
+#include "shill/callbacks.h"
+#include "shill/dbus_properties.h"
+
+namespace shill {
+
+class Error;
+
+// These are the methods that a ModemManager.Modem.Simple proxy must
+// support. The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously.
+class ModemSimpleProxyInterface {
+ public:
+ virtual ~ModemSimpleProxyInterface() {}
+
+ virtual void GetModemStatus(Error *error,
+ const DBusPropertyMapCallback &callback,
+ int timeout) = 0;
+ virtual void Connect(const DBusPropertiesMap &properties,
+ Error *error, const ResultCallback &callback,
+ int timeout) = 0;
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_MODEM_SIMPLE_PROXY_INTERFACE_H_
diff --git a/cellular/modem_unittest.cc b/cellular/modem_unittest.cc
new file mode 100644
index 0000000..e0434fa
--- /dev/null
+++ b/cellular/modem_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2012 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 <vector>
+
+#include <base/strings/stringprintf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <mm/mm-modem.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "shill/cellular/cellular.h"
+#include "shill/cellular/cellular_capability_gsm.h"
+#include "shill/cellular/mock_cellular.h"
+#include "shill/cellular/mock_modem.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/dbus_property_matchers.h"
+#include "shill/event_dispatcher.h"
+#include "shill/manager.h"
+#include "shill/mock_device_info.h"
+#include "shill/net/mock_rtnl_handler.h"
+#include "shill/net/rtnl_handler.h"
+#include "shill/proxy_factory.h"
+
+using std::string;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::StrEq;
+using testing::StrictMock;
+using testing::Test;
+
+namespace shill {
+
+namespace {
+
+const int kTestInterfaceIndex = 5;
+const char kLinkName[] = "usb0";
+const char kOwner[] = ":1.18";
+const char kService[] = "org.chromium.ModemManager";
+const char kPath[] = "/org/chromium/ModemManager/Gobi/0";
+const unsigned char kAddress[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+const char kAddressAsString[] = "000102030405";
+
+} // namespace
+
+class ModemTest : public Test {
+ public:
+ ModemTest()
+ : modem_info_(nullptr, &dispatcher_, nullptr, nullptr, nullptr),
+ device_info_(modem_info_.control_interface(), modem_info_.dispatcher(),
+ modem_info_.metrics(), modem_info_.manager()),
+ modem_(
+ new StrictModem(
+ kOwner,
+ kService,
+ kPath,
+ &modem_info_)) {}
+ virtual void SetUp();
+ virtual void TearDown();
+
+ void ReplaceSingletons() {
+ modem_->rtnl_handler_ = &rtnl_handler_;
+ }
+
+ protected:
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ MockDeviceInfo device_info_;
+ std::unique_ptr<StrictModem> modem_;
+ MockRTNLHandler rtnl_handler_;
+ ByteString expected_address_;
+};
+
+void ModemTest::SetUp() {
+ EXPECT_EQ(kOwner, modem_->owner_);
+ EXPECT_EQ(kService, modem_->service_);
+ EXPECT_EQ(kPath, modem_->path_);
+ ReplaceSingletons();
+ expected_address_ = ByteString(kAddress, arraysize(kAddress));
+
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(kLinkName)).
+ WillRepeatedly(Return(kTestInterfaceIndex));
+
+ EXPECT_CALL(*modem_info_.mock_manager(), device_info())
+ .WillRepeatedly(Return(&device_info_));
+}
+
+void ModemTest::TearDown() {
+ modem_.reset();
+}
+
+TEST_F(ModemTest, PendingDevicePropertiesAndCreate) {
+ static const char kSentinel[] = "sentinel";
+ static const uint32_t kSentinelValue = 17;
+
+ DBusInterfaceToProperties properties;
+ properties[MM_MODEM_INTERFACE][kSentinel].writer().append_uint32(
+ kSentinelValue);
+
+ EXPECT_CALL(*modem_, GetLinkName(_, _)).WillRepeatedly(DoAll(
+ SetArgumentPointee<1>(string(kLinkName)),
+ Return(true)));
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))).
+ WillRepeatedly(Return(kTestInterfaceIndex));
+
+ // The first time we call CreateDeviceFromModemProperties,
+ // GetMACAddress will fail.
+ EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _)).
+ WillOnce(Return(false));
+ EXPECT_CALL(*modem_, GetModemInterface()).
+ WillRepeatedly(Return(MM_MODEM_INTERFACE));
+ modem_->CreateDeviceFromModemProperties(properties);
+ EXPECT_FALSE(modem_->device_.get());
+
+ // On the second time, we allow GetMACAddress to succeed. Now we
+ // expect a device to be built
+ EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _)).
+ WillOnce(DoAll(SetArgumentPointee<1>(expected_address_),
+ Return(true)));
+
+ // modem will take ownership
+ MockCellular *cellular = new MockCellular(
+ &modem_info_,
+ kLinkName,
+ kAddressAsString,
+ kTestInterfaceIndex,
+ Cellular::kTypeCDMA,
+ kOwner,
+ kService,
+ kPath,
+ ProxyFactory::GetInstance());
+
+ EXPECT_CALL(*modem_,
+ ConstructCellular(StrEq(kLinkName),
+ StrEq(kAddressAsString),
+ kTestInterfaceIndex)).
+ WillOnce(Return(cellular));
+
+ EXPECT_CALL(*cellular, OnDBusPropertiesChanged(
+ _,
+ HasDBusPropertyWithValueU32(kSentinel, kSentinelValue),
+ _));
+ EXPECT_CALL(device_info_, RegisterDevice(_));
+ modem_->OnDeviceInfoAvailable(kLinkName);
+
+ EXPECT_TRUE(modem_->device_.get());
+
+ // Add expectations for the eventual |modem_| destruction.
+ EXPECT_CALL(*cellular, DestroyService());
+ EXPECT_CALL(device_info_, DeregisterDevice(_));
+}
+
+TEST_F(ModemTest, EarlyDeviceProperties) {
+ // OnDeviceInfoAvailable called before
+ // CreateDeviceFromModemProperties: Do nothing
+ modem_->OnDeviceInfoAvailable(kLinkName);
+ EXPECT_FALSE(modem_->device_.get());
+}
+
+TEST_F(ModemTest, CreateDeviceEarlyFailures) {
+ DBusInterfaceToProperties properties;
+
+ EXPECT_CALL(*modem_, ConstructCellular(_, _, _)).Times(0);
+ EXPECT_CALL(*modem_, GetModemInterface()).
+ WillRepeatedly(Return(MM_MODEM_INTERFACE));
+
+ // No modem interface properties: no device created
+ modem_->CreateDeviceFromModemProperties(properties);
+ EXPECT_FALSE(modem_->device_.get());
+
+ properties[MM_MODEM_INTERFACE] = DBusPropertiesMap();
+
+ // Link name, but no ifindex: no device created
+ EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(DoAll(
+ SetArgumentPointee<1>(string(kLinkName)),
+ Return(true)));
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))).WillOnce(
+ Return(-1));
+ modem_->CreateDeviceFromModemProperties(properties);
+ EXPECT_FALSE(modem_->device_.get());
+
+ // The params are good, but the device is blacklisted.
+ EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(DoAll(
+ SetArgumentPointee<1>(string(kLinkName)),
+ Return(true)));
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName)))
+ .WillOnce(Return(kTestInterfaceIndex));
+ EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(expected_address_),
+ Return(true)));
+ EXPECT_CALL(device_info_, IsDeviceBlackListed(kLinkName))
+ .WillRepeatedly(Return(true));
+ modem_->CreateDeviceFromModemProperties(properties);
+ EXPECT_FALSE(modem_->device_.get());
+
+ // No link name: see CreateDevicePPP.
+}
+
+TEST_F(ModemTest, CreateDevicePPP) {
+ DBusInterfaceToProperties properties;
+ properties[MM_MODEM_INTERFACE] = DBusPropertiesMap();
+
+ string dev_name(
+ base::StringPrintf(Modem::kFakeDevNameFormat, Modem::fake_dev_serial_));
+
+ // |modem_| will take ownership.
+ MockCellular *cellular = new MockCellular(
+ &modem_info_,
+ dev_name,
+ Modem::kFakeDevAddress,
+ Modem::kFakeDevInterfaceIndex,
+ Cellular::kTypeUniversal,
+ kOwner,
+ kService,
+ kPath,
+ ProxyFactory::GetInstance());
+
+ EXPECT_CALL(*modem_, GetModemInterface()).
+ WillRepeatedly(Return(MM_MODEM_INTERFACE));
+ // No link name: assumed to be a PPP dongle.
+ EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(Return(false));
+ EXPECT_CALL(*modem_,
+ ConstructCellular(dev_name,
+ StrEq(Modem::kFakeDevAddress),
+ Modem::kFakeDevInterfaceIndex)).
+ WillOnce(Return(cellular));
+ EXPECT_CALL(device_info_, RegisterDevice(_));
+
+ modem_->CreateDeviceFromModemProperties(properties);
+ EXPECT_TRUE(modem_->device_.get());
+
+ // Add expectations for the eventual |modem_| destruction.
+ EXPECT_CALL(*cellular, DestroyService());
+ EXPECT_CALL(device_info_, DeregisterDevice(_));
+}
+
+TEST_F(ModemTest, GetDeviceParams) {
+ string mac_address;
+ int interface_index = 2;
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(-1));
+ EXPECT_CALL(device_info_, GetMACAddress(_, _)).Times(AnyNumber())
+ .WillRepeatedly(Return(false));
+ EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index));
+ EXPECT_EQ(-1, interface_index);
+
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(-2));
+ EXPECT_CALL(device_info_, GetMACAddress(_, _)).Times(AnyNumber())
+ .WillRepeatedly(Return(false));
+ EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index));
+ EXPECT_EQ(-2, interface_index);
+
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(1));
+ EXPECT_CALL(device_info_, GetMACAddress(_, _)).WillOnce(Return(false));
+ EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index));
+ EXPECT_EQ(1, interface_index);
+
+ EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(2));
+ EXPECT_CALL(device_info_, GetMACAddress(2, _)).
+ WillOnce(DoAll(SetArgumentPointee<1>(expected_address_),
+ Return(true)));
+ EXPECT_TRUE(modem_->GetDeviceParams(&mac_address, &interface_index));
+ EXPECT_EQ(2, interface_index);
+ EXPECT_EQ(kAddressAsString, mac_address);
+}
+
+TEST_F(ModemTest, RejectPPPModem) {
+ // TODO(rochberg): Port this to ModemClassic
+}
+
+} // namespace shill
diff --git a/cellular/no_out_of_credits_detector.h b/cellular/no_out_of_credits_detector.h
new file mode 100644
index 0000000..8958796
--- /dev/null
+++ b/cellular/no_out_of_credits_detector.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_CELLULAR_NO_OUT_OF_CREDITS_DETECTOR_H_
+#define SHILL_CELLULAR_NO_OUT_OF_CREDITS_DETECTOR_H_
+
+#include <base/macros.h>
+
+#include "shill/cellular/out_of_credits_detector.h"
+
+namespace shill {
+
+// This object performs no out-of-credits detection.
+class NoOutOfCreditsDetector : public OutOfCreditsDetector {
+ public:
+ NoOutOfCreditsDetector(EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service)
+ : OutOfCreditsDetector(dispatcher, manager, metrics, service) {}
+ ~NoOutOfCreditsDetector() override {}
+
+ // Resets the detector state.
+ void ResetDetector() override {}
+ // Returns |true| if this object is busy detecting out-of-credits.
+ bool IsDetecting() const override { return false; }
+ // Notifies this object of a service state change.
+ void NotifyServiceStateChanged(
+ Service::ConnectState old_state,
+ Service::ConnectState new_state) override {}
+ // Notifies this object when the subscription state has changed.
+ void NotifySubscriptionStateChanged(uint32_t subscription_state) override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NoOutOfCreditsDetector);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_NO_OUT_OF_CREDITS_DETECTOR_H_
diff --git a/cellular/out_of_credits_detector.cc b/cellular/out_of_credits_detector.cc
new file mode 100644
index 0000000..fb6bac5
--- /dev/null
+++ b/cellular/out_of_credits_detector.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 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/out_of_credits_detector.h"
+
+#include <string>
+
+#include "shill/cellular/active_passive_out_of_credits_detector.h"
+#include "shill/cellular/cellular_service.h"
+#include "shill/cellular/no_out_of_credits_detector.h"
+#include "shill/cellular/subscription_state_out_of_credits_detector.h"
+#include "shill/logging.h"
+
+namespace shill {
+
+using std::string;
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularService *c) { return c->GetRpcIdentifier(); }
+}
+
+OutOfCreditsDetector::OutOfCreditsDetector(EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service)
+ : dispatcher_(dispatcher),
+ manager_(manager),
+ metrics_(metrics),
+ service_(service),
+ out_of_credits_(false) {
+}
+
+OutOfCreditsDetector::~OutOfCreditsDetector() {
+}
+
+// static
+OutOfCreditsDetector *
+OutOfCreditsDetector::CreateDetector(OOCType detector_type,
+ EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service) {
+ switch (detector_type) {
+ case OOCTypeActivePassive:
+ LOG(INFO) << __func__
+ << ": Using active-passive out-of-credits detection";
+ return
+ new ActivePassiveOutOfCreditsDetector(dispatcher,
+ manager,
+ metrics,
+ service);
+ case OOCTypeSubscriptionState:
+ LOG(INFO) << __func__
+ << ": Using subscription status out-of-credits detection";
+ return
+ new SubscriptionStateOutOfCreditsDetector(dispatcher,
+ manager,
+ metrics,
+ service);
+ default:
+ LOG(INFO) << __func__ << ": No out-of-credits detection";
+ return
+ new NoOutOfCreditsDetector(dispatcher,
+ manager,
+ metrics,
+ service);
+ }
+}
+
+void OutOfCreditsDetector::ReportOutOfCredits(bool state) {
+ SLOG(service_, 2) << __func__ << ": " << state;
+ if (state == out_of_credits_) {
+ return;
+ }
+ out_of_credits_ = state;
+ service_->SignalOutOfCreditsChanged(state);
+}
+
+} // namespace shill
diff --git a/cellular/out_of_credits_detector.h b/cellular/out_of_credits_detector.h
new file mode 100644
index 0000000..8238d9b
--- /dev/null
+++ b/cellular/out_of_credits_detector.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_CELLULAR_OUT_OF_CREDITS_DETECTOR_H_
+#define SHILL_CELLULAR_OUT_OF_CREDITS_DETECTOR_H_
+
+#include <base/macros.h>
+
+#include <string>
+
+#include "shill/service.h"
+
+namespace shill {
+
+class CellularService;
+class EventDispatcher;
+class Manager;
+class Metrics;
+class TrafficMonitor;
+
+// Base class for the various out-of-credits detection mechanism.
+class OutOfCreditsDetector {
+ public:
+ OutOfCreditsDetector(EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service);
+ virtual ~OutOfCreditsDetector();
+
+ // Various types of out-of-credits detections.
+ enum OOCType {
+ // No out-of-credits detection is employed.
+ OOCTypeNone = 0,
+ // Passively monitors the traffic for TX congestion and DNS failures, then
+ // actively probe the network for TX congestion to determine if the
+ // network has entered an OOC condition.
+ OOCTypeActivePassive = 1,
+ // Use ModemManager SubscriptionState property to determine OOC condition.
+ OOCTypeSubscriptionState = 2
+ };
+
+ // Creates a specific out-of-credits detector.
+ // For OOCTypeNone, this methods returns NoOutOfCreditsDetector. For
+ // OOCTypeActivePassive, this method returns
+ // ActivePassiveOutOfCreditsDetector. For OOCTypeSubscriptionState,
+ // this method returns SubscriptionStateOutOfCreditsDetector.
+ static OutOfCreditsDetector *CreateDetector(OOCType detector_type,
+ EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service);
+
+ // Resets the detector state.
+ virtual void ResetDetector() = 0;
+ // Returns |true| if this object is busy detecting out-of-credits.
+ virtual bool IsDetecting() const = 0;
+ // Notifies this object of a service state change.
+ virtual void NotifyServiceStateChanged(Service::ConnectState old_state,
+ Service::ConnectState new_state) = 0;
+ // Notifies this object when the subscription state has changed.
+ virtual void NotifySubscriptionStateChanged(uint32_t subscription_state) = 0;
+
+ virtual bool out_of_credits() const { return out_of_credits_; }
+
+ protected:
+ FRIEND_TEST(ActivePassiveOutOfCreditsDetectorTest,
+ ConnectDisconnectLoopDetectionSkippedAlreadyOutOfCredits);
+
+ // Sets the out-of-credits state for this object and also tells the service
+ // object to signal the property change.
+ void ReportOutOfCredits(bool state);
+
+ // Property accessors reserved for subclasses.
+ EventDispatcher *dispatcher() const { return dispatcher_; }
+ Manager *manager() const { return manager_; }
+ Metrics *metrics() const { return metrics_; }
+ CellularService *service() const { return service_; }
+
+ private:
+ EventDispatcher *dispatcher_;
+ Manager *manager_;
+ Metrics *metrics_;
+ CellularService *service_;
+ // Flag indicating if the account is out-of-credits.
+ bool out_of_credits_;
+
+ DISALLOW_COPY_AND_ASSIGN(OutOfCreditsDetector);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_OUT_OF_CREDITS_DETECTOR_H_
diff --git a/cellular/subscription_state_out_of_credits_detector.cc b/cellular/subscription_state_out_of_credits_detector.cc
new file mode 100644
index 0000000..5cea687
--- /dev/null
+++ b/cellular/subscription_state_out_of_credits_detector.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 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/subscription_state_out_of_credits_detector.h"
+
+#include <string>
+
+#include "ModemManager/ModemManager.h"
+
+#include "shill/cellular/cellular_service.h"
+#include "shill/logging.h"
+
+using std::string;
+
+namespace shill {
+
+namespace Logging {
+static auto kModuleLogScope = ScopeLogger::kCellular;
+static string ObjectID(CellularService *c) { return c->GetRpcIdentifier(); }
+}
+
+SubscriptionStateOutOfCreditsDetector::SubscriptionStateOutOfCreditsDetector(
+ EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service)
+ : OutOfCreditsDetector(dispatcher, manager, metrics, service) {
+}
+
+SubscriptionStateOutOfCreditsDetector::
+ ~SubscriptionStateOutOfCreditsDetector() {
+}
+
+void SubscriptionStateOutOfCreditsDetector::NotifySubscriptionStateChanged(
+ uint32_t subscription_state) {
+ bool ooc = (static_cast<MMModem3gppSubscriptionState>(subscription_state) ==
+ MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA);
+ if (ooc != out_of_credits()) {
+ if (ooc)
+ SLOG(service(), 2) << "Marking service out-of-credits";
+ else
+ SLOG(service(), 2) << "Marking service as not out-of-credits";
+ }
+ ReportOutOfCredits(ooc);
+}
+
+} // namespace shill
diff --git a/cellular/subscription_state_out_of_credits_detector.h b/cellular/subscription_state_out_of_credits_detector.h
new file mode 100644
index 0000000..aed85b9
--- /dev/null
+++ b/cellular/subscription_state_out_of_credits_detector.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_CELLULAR_SUBSCRIPTION_STATE_OUT_OF_CREDITS_DETECTOR_H_
+#define SHILL_CELLULAR_SUBSCRIPTION_STATE_OUT_OF_CREDITS_DETECTOR_H_
+
+#include "shill/cellular/out_of_credits_detector.h"
+
+namespace shill {
+
+// Detects out-of-credits condition by using the subscription state.
+class SubscriptionStateOutOfCreditsDetector : public OutOfCreditsDetector {
+ public:
+ SubscriptionStateOutOfCreditsDetector(EventDispatcher *dispatcher,
+ Manager *manager,
+ Metrics *metrics,
+ CellularService *service);
+ ~SubscriptionStateOutOfCreditsDetector() override;
+
+ void ResetDetector() override {}
+ bool IsDetecting() const override { return false; }
+ void NotifyServiceStateChanged(
+ Service::ConnectState old_state,
+ Service::ConnectState new_state) override {}
+ void NotifySubscriptionStateChanged(uint32_t subscription_state) override;
+
+ private:
+ friend class SubscriptionStateOutOfCreditsDetectorTest;
+
+ DISALLOW_COPY_AND_ASSIGN(SubscriptionStateOutOfCreditsDetector);
+};
+
+} // namespace shill
+
+#endif // SHILL_CELLULAR_SUBSCRIPTION_STATE_OUT_OF_CREDITS_DETECTOR_H_
diff --git a/cellular/subscription_state_out_of_credits_detector_unittest.cc b/cellular/subscription_state_out_of_credits_detector_unittest.cc
new file mode 100644
index 0000000..6720316
--- /dev/null
+++ b/cellular/subscription_state_out_of_credits_detector_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2013 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/subscription_state_out_of_credits_detector.h"
+
+#include <memory>
+
+#include <gtest/gtest.h>
+#include "ModemManager/ModemManager.h"
+
+#include "shill/cellular/mock_cellular.h"
+#include "shill/cellular/mock_cellular_service.h"
+#include "shill/cellular/mock_modem_info.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_connection.h"
+#include "shill/mock_connection_health_checker.h"
+#include "shill/mock_device_info.h"
+#include "shill/mock_manager.h"
+#include "shill/mock_proxy_factory.h"
+#include "shill/mock_traffic_monitor.h"
+
+using testing::Mock;
+using testing::NiceMock;
+
+namespace shill {
+
+class SubscriptionStateOutOfCreditsDetectorTest : public testing::Test {
+ public:
+ SubscriptionStateOutOfCreditsDetectorTest()
+ : modem_info_(nullptr, &dispatcher_, &metrics_, &manager_, nullptr),
+ device_info_(modem_info_.control_interface(), modem_info_.dispatcher(),
+ modem_info_.metrics(), modem_info_.manager()),
+ manager_(modem_info_.control_interface(), modem_info_.dispatcher(),
+ modem_info_.metrics(), modem_info_.glib()),
+ metrics_(modem_info_.dispatcher()),
+ cellular_(new NiceMock<MockCellular>(&modem_info_,
+ "usb0",
+ kAddress,
+ 3,
+ Cellular::kTypeCDMA,
+ "",
+ "",
+ "",
+ ProxyFactory::GetInstance())),
+ service_(new NiceMock<MockCellularService>(&modem_info_, cellular_)),
+ connection_(new NiceMock<MockConnection>(&device_info_)),
+ out_of_credits_detector_(
+ new SubscriptionStateOutOfCreditsDetector(
+ modem_info_.dispatcher(), modem_info_.manager(),
+ modem_info_.metrics(), service_)) {}
+
+ virtual void SetUp() {
+ service_->connection_ = connection_;
+ cellular_->service_ = service_;
+ service_->SetRoamingState(kRoamingStateHome);
+ }
+
+ virtual void TearDown() {
+ cellular_->service_ = nullptr; // Break circular reference.
+ }
+
+ protected:
+ static const char kAddress[];
+
+ EventDispatcher dispatcher_;
+ MockModemInfo modem_info_;
+ NiceMock<MockDeviceInfo> device_info_;
+ NiceMock<MockManager> manager_;
+ NiceMock<MockMetrics> metrics_;
+ scoped_refptr<NiceMock<MockCellular>> cellular_;
+ scoped_refptr<NiceMock<MockCellularService>> service_;
+ scoped_refptr<NiceMock<MockConnection>> connection_;
+ std::unique_ptr<SubscriptionStateOutOfCreditsDetector>
+ out_of_credits_detector_;
+};
+
+const char
+ SubscriptionStateOutOfCreditsDetectorTest::kAddress[] = "000102030405";
+
+TEST_F(SubscriptionStateOutOfCreditsDetectorTest, OutOfCreditsDetection) {
+ out_of_credits_detector_->NotifySubscriptionStateChanged(
+ MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA);
+ EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
+ out_of_credits_detector_->NotifySubscriptionStateChanged(
+ MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED);
+ EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
+}
+
+} // namespace shill