hermes: Inhibit during all eSIM operations.
Motivation:
1) Notifications after profile operations were previously disabled.
However, due to certification requirements, these need to be sent. To
send notifications, we need to acquire a channel after a profile
operation. However, MM is starting around the same time, and there is a
race to acquire a channel. Thus, we need to inhibit MM until
notifications are sent.
2) Inhibiting MM while Hermes effectively acquires a lock on the modem.
It prevents fw updates and slot switches while Hermes is active.
3) Inhibit and Uninhibiting makes MM start from a clean state after
eSIM operations. MM was previously required to reprobe after a slot
switch or a profile switch. Since uninhibiting involves a reprobe,
this is no longer a requirement.
BUG=b:202401139
TEST=tast -verbose run ${cr} cellular.HermesMultiProfile
Cq-Depend: chromium:3379869, chromium:3379870
Change-Id: I68b9b186cec2ce911f9585797b671d859303b85f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/3330727
Reviewed-by: Eric Caruso <ejcaruso@chromium.org>
Tested-by: Pavan Holla <pholla@google.com>
Commit-Queue: Pavan Holla <pholla@google.com>
diff --git a/hermes/BUILD.gn b/hermes/BUILD.gn
index 7846160..136a349 100644
--- a/hermes/BUILD.gn
+++ b/hermes/BUILD.gn
@@ -72,6 +72,7 @@
"daemon.cc",
"euicc.cc",
"euicc_dbus_adaptor.cc",
+ "euicc_event.cc",
"executor.cc",
"hermes_common.cc",
"lpa_util.cc",
diff --git a/hermes/euicc.cc b/hermes/euicc.cc
index bc30da1..d0909e4 100644
--- a/hermes/euicc.cc
+++ b/hermes/euicc.cc
@@ -29,19 +29,12 @@
const char kDefaultProdRootSmds[] = "lpa.ds.gsma.com";
const char kDefaultTestRootSmds[] = "testrootsmds.example.com";
-template <typename... T>
-void RunOnSuccess(base::OnceCallback<void(DbusResult<T...>)> cb,
- DbusResult<T...> dbus_result,
- int err) {
+void PrintEuiccEventResult(int err) {
if (err) {
- LOG(ERROR) << "Received modem error: " << err;
- auto decoded_error = brillo::Error::Create(
- FROM_HERE, brillo::errors::dbus::kDomain, kErrorUnknown,
- "QMI/MBIM operation failed with code: " + std::to_string(err));
- dbus_result.Error(decoded_error);
+ LOG(ERROR) << "ProcessEuiccEvent failed with err=" << err;
return;
}
- std::move(cb).Run(std::move(dbus_result));
+ VLOG(2) << "ProcessEuiccEvent succeeded";
}
} // namespace
@@ -91,10 +84,11 @@
auto download_profile =
base::BindOnce(&Euicc::DownloadProfile, weak_factory_.GetWeakPtr(),
std::move(activation_code), std::move(confirmation_code));
- context_->modem_control()->StoreAndSetActiveSlot(
- physical_slot_,
- base::BindOnce(&RunOnSuccess<dbus::ObjectPath>,
- std::move(download_profile), std::move(dbus_result)));
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START},
+ base::BindOnce(&Euicc::RunOnSuccess<dbus::ObjectPath>,
+ weak_factory_.GetWeakPtr(), std::move(download_profile),
+ std::move(dbus_result)));
}
void Euicc::DownloadProfile(std::string activation_code,
@@ -178,9 +172,10 @@
auto delete_profile =
base::BindOnce(&Euicc::DeleteProfile, weak_factory_.GetWeakPtr(),
std::move(profile_path), matching_profile->GetIccid());
- context_->modem_control()->StoreAndSetActiveSlot(
- physical_slot_, base::BindOnce(&RunOnSuccess<>, std::move(delete_profile),
- std::move(dbus_result)));
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START},
+ base::BindOnce(&Euicc::RunOnSuccess<>, weak_factory_.GetWeakPtr(),
+ std::move(delete_profile), std::move(dbus_result)));
}
void Euicc::DeleteProfile(dbus::ObjectPath profile_path,
@@ -218,7 +213,7 @@
LOG(INFO) << __func__;
auto decoded_error = LpaErrorToBrillo(FROM_HERE, error);
if (decoded_error) {
- dbus_result.Error(decoded_error);
+ EndEuiccOp(dbus_result, std::move(decoded_error));
return;
}
@@ -245,9 +240,10 @@
}
if (!profile) {
- dbus_result.Error(brillo::Error::Create(
+ auto profile_error = brillo::Error::Create(
FROM_HERE, brillo::errors::dbus::kDomain, kErrorInternalLpaFailure,
- "Failed to create Profile object"));
+ "Failed to create Profile object");
+ EndEuiccOp(dbus_result, std::move(profile_error));
return;
}
@@ -267,9 +263,9 @@
// Send notifications has completed, refresh the profile cache.
context_->lpa()->GetInstalledProfiles(
context_->executor(),
- [dbus_result{std::move(dbus_result)}, profile_path](
+ [this, dbus_result{std::move(dbus_result)}, profile_path](
std::vector<lpa::proto::ProfileInfo>& profile_infos,
- int /*error*/) { dbus_result.Success(profile_path); });
+ int /*error*/) { EndEuiccOp(dbus_result, profile_path); });
});
}
@@ -287,7 +283,7 @@
LOG(INFO) << __func__;
auto decoded_error = LpaErrorToBrillo(FROM_HERE, error);
if (decoded_error) {
- dbus_result.Error(decoded_error);
+ EndEuiccOp(dbus_result, std::move(decoded_error));
return;
}
@@ -311,9 +307,9 @@
[this, dbus_result{std::move(dbus_result)}](int /*err*/) {
context_->lpa()->GetInstalledProfiles(
context_->executor(),
- [dbus_result{std::move(dbus_result)}](
+ [this, dbus_result{std::move(dbus_result)}](
std::vector<lpa::proto::ProfileInfo>& profile_infos,
- int /*error*/) { dbus_result.Success(); });
+ int /*error*/) { EndEuiccOp(dbus_result); });
});
}
@@ -329,9 +325,10 @@
}
auto get_installed_profiles =
base::BindOnce(&Euicc::GetInstalledProfiles, weak_factory_.GetWeakPtr());
- context_->modem_control()->StoreAndSetActiveSlot(
- physical_slot_,
- base::BindOnce(&RunOnSuccess<>, std::move(get_installed_profiles),
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START},
+ base::BindOnce(&Euicc::RunOnSuccess<>, weak_factory_.GetWeakPtr(),
+ std::move(get_installed_profiles),
std::move(dbus_result)));
}
@@ -354,7 +351,7 @@
auto decoded_error = LpaErrorToBrillo(FROM_HERE, error);
if (decoded_error) {
LOG(ERROR) << "Failed to retrieve installed profiles";
- dbus_result.Error(decoded_error);
+ EndEuiccOp(dbus_result, std::move(decoded_error));
return;
}
installed_profiles_.clear();
@@ -372,7 +369,7 @@
}
}
UpdateInstalledProfilesProperty();
- dbus_result.Success();
+ EndEuiccOp(dbus_result);
}
void Euicc::RequestPendingProfiles(DbusResult<> dbus_result,
@@ -390,9 +387,10 @@
auto get_pending_profiles_from_smds =
base::BindOnce(&Euicc::GetPendingProfilesFromSmds,
weak_factory_.GetWeakPtr(), std::move(root_smds));
- context_->modem_control()->StoreAndSetActiveSlot(
- physical_slot_,
- base::BindOnce(&RunOnSuccess<>, std::move(get_pending_profiles_from_smds),
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START},
+ base::BindOnce(&Euicc::RunOnSuccess<>, weak_factory_.GetWeakPtr(),
+ std::move(get_pending_profiles_from_smds),
std::move(dbus_result)));
}
@@ -418,7 +416,7 @@
auto decoded_error = LpaErrorToBrillo(FROM_HERE, error);
if (decoded_error) {
LOG(ERROR) << "Failed to retrieve pending profiles";
- dbus_result.Error(decoded_error);
+ EndEuiccOp(dbus_result, std::move(decoded_error));
return;
}
@@ -434,7 +432,7 @@
}
}
UpdatePendingProfilesProperty();
- dbus_result.Success();
+ EndEuiccOp(dbus_result);
}
void Euicc::SetTestModeHelper(bool is_test_mode, DbusResult<> dbus_result) {
@@ -442,9 +440,10 @@
is_test_mode_ = is_test_mode;
auto set_test_mode_internal = base::BindOnce(
&Euicc::SetTestMode, weak_factory_.GetWeakPtr(), is_test_mode);
- context_->modem_control()->StoreAndSetActiveSlot(
- physical_slot_,
- base::BindOnce(&RunOnSuccess<>, std::move(set_test_mode_internal),
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START},
+ base::BindOnce(&Euicc::RunOnSuccess<>, weak_factory_.GetWeakPtr(),
+ std::move(set_test_mode_internal),
std::move(dbus_result)));
}
@@ -452,13 +451,13 @@
VLOG(2) << __func__ << " : is_test_mode" << is_test_mode;
context_->lpa()->SetTestMode(
is_test_mode, context_->executor(),
- [dbus_result{std::move(dbus_result)}](int error) {
+ [this, dbus_result{std::move(dbus_result)}](int error) {
auto decoded_error = LpaErrorToBrillo(FROM_HERE, error);
if (decoded_error) {
- dbus_result.Error(decoded_error);
+ EndEuiccOp(dbus_result, std::move(decoded_error));
return;
}
- dbus_result.Success();
+ EndEuiccOp(dbus_result);
});
}
@@ -483,10 +482,10 @@
auto reset_memory_internal = base::BindOnce(
&Euicc::ResetMemory, weak_factory_.GetWeakPtr(), reset_options);
- context_->modem_control()->StoreAndSetActiveSlot(
- physical_slot_,
- base::BindOnce(&RunOnSuccess<>, std::move(reset_memory_internal),
- std::move(dbus_result)));
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START},
+ base::BindOnce(&Euicc::RunOnSuccess<>, weak_factory_.GetWeakPtr(),
+ std::move(reset_memory_internal), std::move(dbus_result)));
}
void Euicc::ResetMemory(int reset_options, DbusResult<> dbus_result) {
@@ -496,7 +495,7 @@
[this, dbus_result{std::move(dbus_result)}](int error) {
auto decoded_error = LpaErrorToBrillo(FROM_HERE, error);
if (decoded_error) {
- dbus_result.Error(decoded_error);
+ EndEuiccOp(dbus_result, std::move(decoded_error));
return;
}
installed_profiles_.clear();
@@ -510,10 +509,10 @@
auto get_euicc_info_1 =
base::BindOnce(&Euicc::GetEuiccInfo1, weak_factory_.GetWeakPtr());
- context_->modem_control()->StoreAndSetActiveSlot(
- physical_slot_,
- base::BindOnce(&RunOnSuccess<bool>, std::move(get_euicc_info_1),
- std::move(dbus_result)));
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START},
+ base::BindOnce(&Euicc::RunOnSuccess<bool>, weak_factory_.GetWeakPtr(),
+ std::move(get_euicc_info_1), std::move(dbus_result)));
}
void Euicc::GetEuiccInfo1(DbusResult<bool> dbus_result) {
@@ -521,24 +520,63 @@
context_->lpa()->GetEuiccInfo1(
context_->executor(),
- [dbus_result{std::move(dbus_result)}](
+ [this, dbus_result{std::move(dbus_result)}](
lpa::proto::EuiccInfo1& euicc_info_1, int error) {
LOG(INFO) << "euicc_info_1:" << euicc_info_1.DebugString();
auto decoded_error = LpaErrorToBrillo(FROM_HERE, error);
if (decoded_error) {
- dbus_result.Error(decoded_error);
+ EndEuiccOp(dbus_result, std::move(decoded_error));
return;
}
constexpr char kTestEuiccInfo1[] =
"665A1433D67C1A2C5DB8B52C967F10A057BA5CB2";
for (const auto& pkid : euicc_info_1.pkid_for_verif()) {
if (pkid == kTestEuiccInfo1) {
- dbus_result.Success(true);
+ EndEuiccOp(dbus_result, true);
return;
}
}
- dbus_result.Success(false);
+ EndEuiccOp(dbus_result, false);
});
}
+template <typename... T>
+void Euicc::EndEuiccOp(DbusResult<T...> dbus_result, T... object) {
+ auto send_dbus_response = base::BindOnce(
+ [](const DbusResult<T...>& dbus_result, const T&... object, int err) {
+ PrintEuiccEventResult(err);
+ dbus_result.Success(object...);
+ },
+ dbus_result, object...);
+ context_->modem_control()->ProcessEuiccEvent({physical_slot_, EuiccStep::END},
+ std::move(send_dbus_response));
+}
+
+template <typename... T>
+void Euicc::EndEuiccOp(DbusResult<T...> dbus_result, brillo::ErrorPtr error) {
+ auto send_dbus_response = base::BindOnce(
+ [](const DbusResult<T...>& dbus_result, brillo::ErrorPtr error, int err) {
+ PrintEuiccEventResult(err);
+ dbus_result.Error(error);
+ },
+ dbus_result, std::move(error));
+ context_->modem_control()->ProcessEuiccEvent({physical_slot_, EuiccStep::END},
+ std::move(send_dbus_response));
+}
+
+template <typename... T>
+void Euicc::RunOnSuccess(base::OnceCallback<void(DbusResult<T...>)> cb,
+ DbusResult<T...> dbus_result,
+ int err) {
+ if (err) {
+ LOG(ERROR) << "Received modem error: " << err;
+ auto decoded_error = brillo::Error::Create(
+ FROM_HERE, brillo::errors::dbus::kDomain, kErrorUnknown,
+ "QMI/MBIM operation failed with code: " + std::to_string(err));
+ EndEuiccOp(dbus_result, std::move(decoded_error));
+ return;
+ }
+ std::move(cb).Run(std::move(dbus_result));
+}
+
} // namespace hermes
diff --git a/hermes/euicc.h b/hermes/euicc.h
index 7e47a36..2e2ffbd 100644
--- a/hermes/euicc.h
+++ b/hermes/euicc.h
@@ -105,6 +105,15 @@
std::vector<std::unique_ptr<Profile>> pending_profiles_;
base::WeakPtrFactory<Euicc> weak_factory_;
+
+ template <typename... T>
+ void EndEuiccOp(DbusResult<T...> dbus_result, T... object);
+ template <typename... T>
+ void EndEuiccOp(DbusResult<T...> dbus_result, brillo::ErrorPtr error);
+ template <typename... T>
+ void RunOnSuccess(base::OnceCallback<void(DbusResult<T...>)> cb,
+ DbusResult<T...> dbus_result,
+ int err);
};
} // namespace hermes
diff --git a/hermes/euicc_event.cc b/hermes/euicc_event.cc
new file mode 100644
index 0000000..3d20707
--- /dev/null
+++ b/hermes/euicc_event.cc
@@ -0,0 +1,58 @@
+// Copyright 2022 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 <ostream>
+
+#include "hermes/euicc_event.h"
+
+namespace hermes {
+
+EuiccEvent::EuiccEvent(uint32_t slot, EuiccStep step, EuiccOp op)
+ : slot(slot), step(step), op(op) {}
+
+EuiccEvent::EuiccEvent(uint32_t slot, EuiccStep step)
+ : slot(slot), step(step), op(EuiccOp::UNKNOWN) {}
+
+std::ostream& operator<<(std::ostream& os, const EuiccStep& rhs) {
+ switch (rhs) {
+ case (EuiccStep::START):
+ os << "START";
+ break;
+ case (EuiccStep::PENDING_NOTIFICATIONS):
+ os << "PENDING_NOTIFICATIONS";
+ break;
+ case (EuiccStep::END):
+ os << "END";
+ break;
+ default:
+ os << rhs;
+ break;
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const EuiccOp& rhs) {
+ switch (rhs) {
+ case (EuiccOp::UNKNOWN):
+ os << "UNKNOWN";
+ break;
+ case (EuiccOp::ENABLE):
+ os << "ENABLE";
+ break;
+ case (EuiccOp::DISABLE):
+ os << "DISABLE";
+ break;
+ default:
+ os << rhs;
+ break;
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const EuiccEvent& rhs) {
+ os << "Slot: " << rhs.slot << ", Step:" << rhs.step << ", Op:" << rhs.op;
+ return os;
+}
+
+} // namespace hermes
diff --git a/hermes/euicc_event.h b/hermes/euicc_event.h
new file mode 100644
index 0000000..d91adb4
--- /dev/null
+++ b/hermes/euicc_event.h
@@ -0,0 +1,33 @@
+// Copyright 2022 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 HERMES_EUICC_EVENT_H_
+#define HERMES_EUICC_EVENT_H_
+
+namespace hermes {
+
+enum class EuiccStep {
+ START,
+ PENDING_NOTIFICATIONS,
+ END,
+};
+
+enum class EuiccOp { UNKNOWN, DISABLE, ENABLE };
+
+struct EuiccEvent {
+ uint32_t slot;
+ EuiccStep step;
+ EuiccOp op;
+
+ EuiccEvent(uint32_t slot, EuiccStep step);
+ EuiccEvent(uint32_t slot, EuiccStep step, EuiccOp op);
+};
+
+std::ostream& operator<<(std::ostream& os, const EuiccStep& rhs);
+std::ostream& operator<<(std::ostream& os, const EuiccOp& rhs);
+std::ostream& operator<<(std::ostream& os, const EuiccEvent& rhs);
+
+} // namespace hermes
+
+#endif // HERMES_EUICC_EVENT_H_
diff --git a/hermes/euicc_interface.h b/hermes/euicc_interface.h
index def7886..2a83248 100644
--- a/hermes/euicc_interface.h
+++ b/hermes/euicc_interface.h
@@ -34,9 +34,8 @@
// from ModemControlInterface
virtual void StoreAndSetActiveSlot(uint32_t physical_slot,
ResultCallback cb) = 0;
+ virtual void ProcessEuiccEvent(EuiccEvent event, ResultCallback cb) = 0;
virtual void RestoreActiveSlot(ResultCallback cb) = 0;
- virtual void StartProfileOp(uint32_t physical_slot, ResultCallback cb) = 0;
- virtual void FinishProfileOp(ResultCallback cb) = 0;
// from lpa::card::EuiccCard
virtual void SendApdus(std::vector<lpa::card::Apdu> apdus,
diff --git a/hermes/hermes_common.h b/hermes/hermes_common.h
index 2cda4ea..9037401 100644
--- a/hermes/hermes_common.h
+++ b/hermes/hermes_common.h
@@ -17,7 +17,14 @@
constexpr char bcd_chars[] = "0123456789\0\0\0\0\0\0";
constexpr auto kLpaRetryDelay = base::TimeDelta::FromSeconds(2);
+// Duration that Chrome waits for Hermes to return a DBus response
+constexpr auto kHermesTimeout = base::TimeDelta::FromMinutes(4);
+
constexpr int kSuccess = 0;
+// This error will be returned when a received mbim/qmi message cannot be parsed
+// or when it is received in an unexpected state.
+constexpr int kModemMessageProcessingError = -1;
+constexpr int kModemManagerError = -2;
std::string GetTrailingChars(const std::string& pii, int num_chars);
// Used to redact PII in logs
diff --git a/hermes/modem.h b/hermes/modem.h
index 8cdf2b2..c9f02c4 100644
--- a/hermes/modem.h
+++ b/hermes/modem.h
@@ -26,11 +26,12 @@
constexpr auto kInitRetryDelay = base::TimeDelta::FromSeconds(10);
constexpr uint8_t kInvalidChannel = 0;
+// Schedule an uninhibit 2 seconds after an eSIM operation succeeds. If another
+// eSIM operation makes an inhibit call during these 2 seconds, the scheduled
+// uninhibit is cancelled.
+constexpr auto kUninhibitDelay = base::TimeDelta::FromSeconds(2);
+
constexpr int kModemSuccess = 0;
-// This error will be returned when a received mbim/qmi message cannot be parsed
-// or when it is received in an unexpected state.
-constexpr int kModemMessageProcessingError = -1;
-constexpr int kModemManagerError = -2;
constexpr int kDefault3GPPRelease = 11;
diff --git a/hermes/modem_control_interface.h b/hermes/modem_control_interface.h
index b967bc1..745ff5f 100644
--- a/hermes/modem_control_interface.h
+++ b/hermes/modem_control_interface.h
@@ -5,6 +5,7 @@
#ifndef HERMES_MODEM_CONTROL_INTERFACE_H_
#define HERMES_MODEM_CONTROL_INTERFACE_H_
+#include "hermes/euicc_event.h"
#include "hermes/hermes_common.h"
namespace hermes {
@@ -18,8 +19,7 @@
// Restore the value stored by StoreAndSetActiveSlot
virtual void RestoreActiveSlot(ResultCallback cb) = 0;
- virtual void StartProfileOp(uint32_t physical_slot, ResultCallback cb) = 0;
- virtual void FinishProfileOp(ResultCallback cb) = 0;
+ virtual void ProcessEuiccEvent(EuiccEvent event, ResultCallback cb) = 0;
virtual ~ModemControlInterface() = default;
};
diff --git a/hermes/modem_manager_proxy.cc b/hermes/modem_manager_proxy.cc
index a511121..6d54397 100644
--- a/hermes/modem_manager_proxy.cc
+++ b/hermes/modem_manager_proxy.cc
@@ -12,10 +12,13 @@
ModemManagerProxy::ModemManagerProxy(const scoped_refptr<dbus::Bus>& bus)
: bus_(bus),
- proxy_(std::make_unique<org::freedesktop::DBus::ObjectManagerProxy>(
- bus,
- modemmanager::kModemManager1ServiceName,
- dbus::ObjectPath(modemmanager::kModemManager1ServicePath))),
+ object_manager_proxy_(
+ std::make_unique<org::freedesktop::DBus::ObjectManagerProxy>(
+ bus,
+ modemmanager::kModemManager1ServiceName,
+ dbus::ObjectPath(modemmanager::kModemManager1ServicePath))),
+ mm_proxy_(std::make_unique<org::freedesktop::ModemManager1Proxy>(
+ bus, modemmanager::kModemManager1ServiceName)),
weak_factory_(this) {
auto on_interface_added = base::BindRepeating(
&ModemManagerProxy::OnInterfaceAdded, weak_factory_.GetWeakPtr());
@@ -26,13 +29,14 @@
LOG(ERROR) << "Failed to connect to signal " << interface << "."
<< signal;
});
- proxy_->RegisterInterfacesAddedSignalHandler(
+ object_manager_proxy_->RegisterInterfacesAddedSignalHandler(
std::move(on_interface_added), std::move(on_dbus_signal_connected));
}
ModemManagerProxy::ModemManagerProxy() : weak_factory_(this) {}
void ModemManagerProxy::RegisterModemAppearedCallback(base::OnceClosure cb) {
+ VLOG(2) << __func__;
on_modem_appeared_cb_ = std::move(cb);
}
@@ -41,7 +45,7 @@
auto on_service_available =
base::BindOnce(&ModemManagerProxy::WaitForModemStepGetObjects,
weak_factory_.GetWeakPtr(), std::move(cb));
- proxy_->GetObjectProxy()->WaitForServiceToBeAvailable(
+ object_manager_proxy_->GetObjectProxy()->WaitForServiceToBeAvailable(
std::move((on_service_available)));
}
@@ -51,7 +55,7 @@
LOG(ERROR) << "Could not get MM managed objects: " << err->GetDomain()
<< ": " << err->GetCode() << ": " << err->GetMessage();
});
- proxy_->GetManagedObjectsAsync(
+ object_manager_proxy_->GetManagedObjectsAsync(
base::BindOnce(&ModemManagerProxy::WaitForModemStepLast,
weak_factory_.GetWeakPtr(), std::move(cb)),
std::move(error_cb));
@@ -99,7 +103,7 @@
void ModemManagerProxy::OnPropertiesChanged(
org::freedesktop::ModemManager1::ModemProxyInterface* /*unused*/,
const std::string& prop) {
- VLOG(2) << __func__ << " : " << prop << " changed.";
+ VLOG(3) << __func__ << " : " << prop << " changed.";
if (!modem_proxy_->GetProperties()->primary_port.is_valid() ||
!modem_proxy_->GetProperties()->device_identifier.is_valid())
return;
@@ -111,4 +115,92 @@
return modem_proxy_->primary_port();
}
+void ModemManagerProxy::Uninhibit() {
+ uninhibit_cb_.Cancel();
+ if (inhibited_uid_.has_value()) {
+ InhibitDevice(false, base::BindOnce([](int err) {
+ VLOG(2) << "Uninhibit completed with err: " << err;
+ }));
+ }
+}
+
+void ModemManagerProxy::ScheduleUninhibit(base::TimeDelta timeout) {
+ LOG(INFO) << "Uninhibiting in " << timeout.InSeconds() << " seconds.";
+ uninhibit_cb_.Cancel();
+
+ uninhibit_cb_.Reset(base::BindOnce(&ModemManagerProxy::Uninhibit,
+ weak_factory_.GetWeakPtr()));
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, uninhibit_cb_.callback(), timeout);
+}
+
+void ModemManagerProxy::WaitForModemAndInhibit(ResultCallback cb) {
+ // wait for modem if another daemon has inhibited MM. If Hermes has inhibited
+ // MM, then we needn't wait for the modem.
+ if (inhibited_uid_.has_value()) {
+ LOG(INFO) << inhibited_uid_.value() << " is already inhibited.";
+ OnInhibitSuccess(/*inhibit*/ true, inhibited_uid_.value(), std::move(cb));
+ return;
+ }
+ WaitForModem(base::BindOnce(&ModemManagerProxy::InhibitDevice,
+ weak_factory_.GetWeakPtr(), true, std::move(cb)));
+}
+
+void ModemManagerProxy::InhibitDevice(bool inhibit, ResultCallback cb) {
+ LOG(INFO) << __func__ << ": inhibit = " << inhibit;
+ if (!inhibit && !inhibited_uid_.has_value()) {
+ LOG(ERROR) << "No inhibited device found.";
+ std::move(cb).Run(kModemManagerError);
+ return;
+ }
+
+ if (inhibit && (!modem_proxy_ || modem_proxy_->device().empty())) {
+ LOG(ERROR) << __func__ << ": Device identifier unavailable.";
+ std::move(cb).Run(kModemManagerError);
+ return;
+ }
+
+ auto uid = inhibit ? modem_proxy_->device() : inhibited_uid_.value();
+
+ constexpr int kInhibitTimeoutMilliseconds = 1000;
+ // convert cb into a repeating callback, so that it can be used by either
+ // on_inhibit_success or on_inhibit_fail
+ auto return_inhibit_result = base::BindRepeating(
+ [](ResultCallback cb, int err) { std::move(cb).Run(err); },
+ base::Passed(std::move(cb)));
+
+ auto on_inhibit_success = base::BindOnce(&ModemManagerProxy::OnInhibitSuccess,
+ weak_factory_.GetWeakPtr(), inhibit,
+ uid, return_inhibit_result);
+ auto on_inhibit_fail = base::BindOnce(
+ [](ResultCallback cb, brillo::Error* error) {
+ LOG(ERROR) << error->GetMessage();
+ std::move(cb).Run(kModemManagerError);
+ },
+ return_inhibit_result);
+
+ mm_proxy_->InhibitDeviceAsync(uid, inhibit, std::move(on_inhibit_success),
+ std::move(on_inhibit_fail),
+ kInhibitTimeoutMilliseconds);
+}
+
+void ModemManagerProxy::OnInhibitSuccess(bool inhibit,
+ std::basic_string<char> uid,
+ ResultCallback cb) {
+ VLOG(2) << __func__;
+ inhibited_uid_ =
+ inhibit ? std::optional<std::basic_string<char>>{uid} : std::nullopt;
+
+ // uninhibit automatically if we exceed the max duration allowed for a
+ // Hermes operation.
+ uninhibit_cb_.Cancel();
+ if (inhibit) {
+ ScheduleUninhibit(kHermesTimeout);
+ std::move(cb).Run(kSuccess);
+ return;
+ }
+
+ std::move(cb).Run(kSuccess);
+}
+
} // namespace hermes
diff --git a/hermes/modem_manager_proxy.h b/hermes/modem_manager_proxy.h
index 1680308..bb12fb9 100644
--- a/hermes/modem_manager_proxy.h
+++ b/hermes/modem_manager_proxy.h
@@ -11,8 +11,10 @@
#include <chromeos/dbus/service_constants.h>
#include <dbus/bus.h>
+#include <base/cancelable_callback.h>
#include "hermes/dbus_bindings/mm-proxies.h"
+#include "hermes/executor.h"
#include "hermes/hermes_common.h"
namespace hermes {
@@ -33,6 +35,9 @@
std::string GetPrimaryPort() const;
+ void ScheduleUninhibit(base::TimeDelta timeout);
+ void WaitForModemAndInhibit(ResultCallback cb);
+
protected:
// To be used by mocks only
ModemManagerProxy();
@@ -49,11 +54,22 @@
org::freedesktop::ModemManager1::ModemProxyInterface* /*unused*/,
const std::string& prop);
+ void Uninhibit();
+ void InhibitDevice(bool inhibit, ResultCallback cb);
+ void OnInhibitSuccess(bool inhibit,
+ std::basic_string<char> uid,
+ ResultCallback cb);
+
scoped_refptr<dbus::Bus> bus_;
- std::unique_ptr<org::freedesktop::DBus::ObjectManagerProxy> proxy_;
+ std::unique_ptr<org::freedesktop::DBus::ObjectManagerProxy>
+ object_manager_proxy_;
+ std::unique_ptr<org::freedesktop::ModemManager1Proxy> mm_proxy_;
std::unique_ptr<org::freedesktop::ModemManager1::ModemProxy> modem_proxy_;
base::OnceClosure on_modem_appeared_cb_;
+ std::optional<std::basic_string<char>> inhibited_uid_;
+ base::CancelableOnceClosure uninhibit_cb_;
+
base::WeakPtrFactory<ModemManagerProxy> weak_factory_;
};
} // namespace hermes
diff --git a/hermes/modem_mbim.cc b/hermes/modem_mbim.cc
index 29e119e..5568ffd 100644
--- a/hermes/modem_mbim.cc
+++ b/hermes/modem_mbim.cc
@@ -332,6 +332,14 @@
TransmitFromQueue();
}
+void ModemMbim::CloseChannel(base::OnceCallback<void(int)> cb) {
+ tx_queue_.push_back(
+ {std::unique_ptr<TxInfo>(), AllocateId(),
+ std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimCloseLogicalChannel),
+ std::move(cb)});
+ TransmitFromQueue();
+}
+
void ModemMbim::AcquireChannel(base::OnceCallback<void(int)> cb) {
LOG(INFO) << __func__;
tx_queue_.push_back(
@@ -346,12 +354,10 @@
LOG(INFO) << __func__ << " with physical_slot: " << physical_slot;
auto acquire_channel =
base::BindOnce(&ModemMbim::AcquireChannel, weak_factory_.GetWeakPtr());
- tx_queue_.push_back(
- {std::unique_ptr<TxInfo>(), AllocateId(),
- std::make_unique<MbimCmd>(MbimCmd::MbimType::kMbimCloseLogicalChannel),
- base::BindOnce(&RunNextStep, std::move(acquire_channel),
- std::move(cb))});
- TransmitFromQueue();
+ // Close any existing channel, and acquire a new one. Run cb after a new
+ // channel has been acquired.
+ CloseChannel(
+ base::BindOnce(&RunNextStep, std::move(acquire_channel), std::move(cb)));
}
void ModemMbim::GetEidFromSim(ResultCallback cb) {
@@ -727,15 +733,33 @@
(GAsyncReadyCallback)MbimCreateNewDeviceCb, this);
}
-void ModemMbim::StartProfileOp(uint32_t physical_slot, ResultCallback cb) {
- LOG(INFO) << __func__ << " physical_slot:" << physical_slot;
- retry_count_ = 0;
- is_ready_state_valid = false;
- StoreAndSetActiveSlot(physical_slot, std::move(cb));
+void ModemMbim::ProcessEuiccEvent(EuiccEvent event, ResultCallback cb) {
+ LOG(INFO) << __func__ << ", " << event;
+ if (event.step == EuiccStep::START) {
+ auto store_and_set_active_slot =
+ base::BindOnce(&ModemMbim::StoreAndSetActiveSlot,
+ weak_factory_.GetWeakPtr(), event.slot);
+ modem_manager_proxy_->WaitForModemAndInhibit(base::BindOnce(
+ &RunNextStep, std::move(store_and_set_active_slot), std::move(cb)));
+ return;
+ }
+ if (event.step == EuiccStep::PENDING_NOTIFICATIONS) {
+ AcquireChannelAfterCardReady(event, std::move(cb));
+ return;
+ }
+ if (event.step == EuiccStep::END) {
+ auto close_device_and_uninhibit = base::BindOnce(
+ &ModemMbim::CloseDeviceAndUninhibit, weak_factory_.GetWeakPtr());
+ // Close channel, followed by close device and uninhibit, and then execute
+ // cb
+ CloseChannel(base::BindOnce(
+ &RunNextStep, std::move(close_device_and_uninhibit), std::move(cb)));
+ return;
+ }
}
-void ModemMbim::FinishProfileOp(ResultCallback cb) {
- LOG(INFO) << __func__;
+void ModemMbim::AcquireChannelAfterCardReady(EuiccEvent event,
+ ResultCallback cb) {
const guint MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE = 7;
if (!is_ready_state_valid ||
!(ready_state_ == MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED ||
@@ -751,18 +775,20 @@
retry_count_++;
executor_->PostDelayedTask(
FROM_HERE,
- base::BindOnce(&ModemMbim::FinishProfileOp, weak_factory_.GetWeakPtr(),
+ base::BindOnce(&ModemMbim::AcquireChannelAfterCardReady,
+ weak_factory_.GetWeakPtr(), std::move(event),
std::move(cb)),
kSimRefreshDelay);
return;
}
retry_count_ = 0;
- // Ideally we would acquire a channel to send notifications here. However,
- // acquiring a channel could cause MM to stop reporting the EID due to a fw
- // bug in L850. Thus we skip sending profile enable/disable notifications
- // until b/195589882, and b/202401139 are fixed.
+ AcquireChannel(std::move(cb));
+}
+
+void ModemMbim::CloseDeviceAndUninhibit(ResultCallback cb) {
CloseDevice();
- std::move(cb).Run(kModemMessageProcessingError);
+ modem_manager_proxy_->ScheduleUninhibit(kUninhibitDelay);
+ std::move(cb).Run(kModemSuccess);
}
void ModemMbim::RestoreActiveSlot(ResultCallback cb) {
diff --git a/hermes/modem_mbim.h b/hermes/modem_mbim.h
index 256520b..f8ea0ad 100644
--- a/hermes/modem_mbim.h
+++ b/hermes/modem_mbim.h
@@ -43,8 +43,7 @@
ResultCallback cb) override;
void StoreAndSetActiveSlot(const uint32_t physical_slot,
ResultCallback cb) override;
- void StartProfileOp(const uint32_t physical_slot, ResultCallback cb) override;
- void FinishProfileOp(ResultCallback cb) override;
+ void ProcessEuiccEvent(EuiccEvent event, ResultCallback cb) override;
void RestoreActiveSlot(ResultCallback cb) override;
bool IsSimValidAfterEnable() override;
bool IsSimValidAfterDisable() override;
@@ -74,6 +73,7 @@
void TransmitMbimSendEidApdu();
void TransmitMbimSendApdu(TxElement* tx_element);
void QueryCurrentMbimCapabilities(ResultCallback cb);
+ void CloseChannel(base::OnceCallback<void(int)> cb);
void AcquireChannel(base::OnceCallback<void(int)> cb);
void ReacquireChannel(const uint32_t physical_slot, ResultCallback cb);
void GetEidFromSim(ResultCallback cb);
@@ -112,6 +112,7 @@
void CloseDevice();
+ void CloseDeviceAndUninhibit(ResultCallback cb);
///////////////////
// State Diagram //
///////////////////
@@ -165,7 +166,11 @@
MbimSubscriberReadyState ready_state_;
GFile* file_ = NULL;
bool is_ready_state_valid;
+
+ base::CancelableOnceClosure scheduled_uninhibit_;
+
base::WeakPtrFactory<ModemMbim> weak_factory_;
+ void AcquireChannelAfterCardReady(EuiccEvent event, ResultCallback cb);
};
} // namespace hermes
diff --git a/hermes/modem_qrtr.cc b/hermes/modem_qrtr.cc
index 3956366..14527c3 100644
--- a/hermes/modem_qrtr.cc
+++ b/hermes/modem_qrtr.cc
@@ -883,21 +883,36 @@
TransmitFromQueue();
}
-void ModemQrtr::StartProfileOp(uint32_t physical_slot, ResultCallback cb) {
- LOG(INFO) << __func__ << " physical_slot:" << physical_slot;
- // The card triggers a refresh after profile enable. This refresh can cause
- // response apdu's with intermediate bytes to be flushed during a qmi
- // transaction. Since, we don't use these intermediate bytes, disable
- // them to avoid qmi errors as per QC's recommendation. b/169954635
- SetProcedureBytes(ProcedureBytesMode::DisableIntermediateBytes);
- StoreAndSetActiveSlot(physical_slot, std::move(cb));
-}
+void ModemQrtr::ProcessEuiccEvent(EuiccEvent event, ResultCallback cb) {
+ LOG(INFO) << __func__ << ", " << event;
+ if (event.step == EuiccStep::START) {
+ if (event.op == EuiccOp::DISABLE || event.op == EuiccOp::ENABLE) {
+ // The card triggers a refresh after profile enable. This refresh can
+ // cause response apdu's with intermediate bytes to be flushed during a
+ // qmi transaction. Since, we don't use these intermediate bytes, disable
+ // them to avoid qmi errors as per QC's recommendation. b/169954635
+ SetProcedureBytes(ProcedureBytesMode::DisableIntermediateBytes);
+ }
-void ModemQrtr::FinishProfileOp(ResultCallback cb) {
- LOG(INFO) << __func__;
- DisableQmi(kSimRefreshDelay);
- SetProcedureBytes(ProcedureBytesMode::EnableIntermediateBytes);
- AcquireChannel(std::move(cb));
+ auto store_and_set_active_slot =
+ base::BindOnce(&ModemQrtr::StoreAndSetActiveSlot,
+ weak_factory_.GetWeakPtr(), event.slot);
+ modem_manager_proxy_->WaitForModemAndInhibit(base::BindOnce(
+ &RunNextStep, std::move(store_and_set_active_slot), std::move(cb)));
+ return;
+ }
+ if (event.step == EuiccStep::PENDING_NOTIFICATIONS) {
+ SetProcedureBytes(ProcedureBytesMode::EnableIntermediateBytes);
+ DisableQmi(kSimRefreshDelay);
+ AcquireChannel(std::move(cb));
+ return;
+ }
+ if (event.step == EuiccStep::END) {
+ SetProcedureBytes(ProcedureBytesMode::EnableIntermediateBytes);
+ modem_manager_proxy_->ScheduleUninhibit(kUninhibitDelay);
+ std::move(cb).Run(kModemSuccess);
+ return;
+ }
}
void ModemQrtr::QrtrTable::Insert(QmiCmdInterface::Service service,
diff --git a/hermes/modem_qrtr.h b/hermes/modem_qrtr.h
index ff82967..06e820c 100644
--- a/hermes/modem_qrtr.h
+++ b/hermes/modem_qrtr.h
@@ -46,9 +46,7 @@
void StoreAndSetActiveSlot(uint32_t physical_slot,
ResultCallback cb) override;
void RestoreActiveSlot(ResultCallback cb) override;
- void StartProfileOp(uint32_t physical_slot, ResultCallback cb) override;
- void FinishProfileOp(ResultCallback cb) override;
-
+ void ProcessEuiccEvent(EuiccEvent event, ResultCallback cb) override;
private:
struct SwitchSlotTxInfo : public TxInfo {
diff --git a/hermes/profile.cc b/hermes/profile.cc
index 58b4eab..0d6b950 100644
--- a/hermes/profile.cc
+++ b/hermes/profile.cc
@@ -52,6 +52,15 @@
}
}
+void SendDBusError(std::shared_ptr<Profile::DBusResponse<>> response,
+ brillo::ErrorPtr lpa_error,
+ int modem_error) {
+ if (modem_error != kSuccess) {
+ LOG(ERROR) << "Modem finished with error code: " << modem_error;
+ }
+ response->ReplyWithError(lpa_error.get());
+}
+
template <typename T>
void RunOnSuccess(base::OnceCallback<void(T)> cb, T response, int err) {
if (err) {
@@ -146,8 +155,8 @@
LOG(INFO) << "Enabling profile: " << GetObjectPathForLog(object_path_);
auto enable_profile =
base::BindOnce(&Profile::EnableProfile, weak_factory_.GetWeakPtr());
- context_->modem_control()->StartProfileOp(
- physical_slot_,
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START, EuiccOp::ENABLE},
base::BindOnce(&RunOnSuccess<std::unique_ptr<DBusResponse<>>>,
std::move(enable_profile), std::move(response)));
}
@@ -185,8 +194,8 @@
LOG(INFO) << "Disabling profile: " << GetObjectPathForLog(object_path_);
auto disable_profile =
base::BindOnce(&Profile::DisableProfile, weak_factory_.GetWeakPtr());
- context_->modem_control()->StartProfileOp(
- physical_slot_,
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START, EuiccOp::DISABLE},
base::BindOnce(&RunOnSuccess<std::unique_ptr<DBusResponse<>>>,
std::move(disable_profile), std::move(response)));
}
@@ -210,7 +219,10 @@
if (decoded_error) {
LOG(ERROR) << "Failed enabling profile: " << object_path_.value()
<< " (error " << decoded_error << ")";
- response->ReplyWithError(decoded_error.get());
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::END},
+ base::BindOnce(&SendDBusError, std::move(response),
+ std::move(decoded_error)));
return;
}
on_profile_enabled_cb_.Run(GetIccid());
@@ -218,7 +230,9 @@
auto send_notifs =
base::BindOnce(&Profile::FinishProfileOpCb, weak_factory_.GetWeakPtr(),
std::move(response));
- context_->modem_control()->FinishProfileOp(std::move(send_notifs));
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::PENDING_NOTIFICATIONS},
+ std::move(send_notifs));
}
void Profile::OnDisabled(int error, std::shared_ptr<DBusResponse<>> response) {
@@ -227,7 +241,10 @@
if (decoded_error) {
LOG(ERROR) << "Failed disabling profile: " << object_path_.value()
<< " (error " << decoded_error << ")";
- response->ReplyWithError(decoded_error.get());
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::END},
+ base::BindOnce(&SendDBusError, std::move(response),
+ std::move(decoded_error)));
return;
}
LOG(INFO) << "Disabled profile: " << object_path_.value();
@@ -236,11 +253,14 @@
auto send_notifs =
base::BindOnce(&Profile::FinishProfileOpCb, weak_factory_.GetWeakPtr(),
std::move(response));
- context_->modem_control()->FinishProfileOp(std::move(send_notifs));
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::PENDING_NOTIFICATIONS},
+ std::move(send_notifs));
}
void Profile::FinishProfileOpCb(std::shared_ptr<DBusResponse<>> response,
int err) {
+ LOG(INFO) << __func__;
if (err) {
LOG(WARNING) << "Could not finish profile op: " << object_path_.value();
// Notifications are optional by the standard. Since FinishProfileOp failed,
@@ -251,7 +271,18 @@
}
context_->lpa()->SendNotifications(
context_->executor(),
- [response{std::move(response)}](int /*error*/) { response->Return(); });
+ [this, response{std::move(response)}](int /*error*/) {
+ VLOG(2) << "FinishProfileOpCb: sent all notifications";
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::END},
+ base::BindOnce(
+ [](std::shared_ptr<DBusResponse<>> response, int error) {
+ response->Return();
+ LOG(INFO)
+ << "FinishProfileOpCb: completed with err = " << error;
+ },
+ response));
+ });
}
void Profile::Rename(std::unique_ptr<DBusResponse<>> response,
@@ -269,8 +300,8 @@
auto set_nickname =
base::BindOnce(&Profile::SetNicknameMethod, weak_factory_.GetWeakPtr(),
std::move(nickname));
- context_->modem_control()->StoreAndSetActiveSlot(
- physical_slot_,
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::START},
base::BindOnce(&RunOnSuccess<std::unique_ptr<DBusResponse<>>>,
std::move(set_nickname), std::move(response)));
}
@@ -322,13 +353,22 @@
return;
}
this->SetNickname(nickname);
- auto report_success =
- base::BindOnce([](std::shared_ptr<DBusResponse<>> response) {
- response->Return();
- });
- context_->modem_control()->RestoreActiveSlot(
- base::BindOnce(&RunOnSuccess<std::shared_ptr<DBusResponse<>>>,
- std::move(report_success), std::move(response)));
+
+ auto restore_slot_and_dbus_return = base::BindOnce(
+ [](ModemControlInterface* modem_control,
+ std::shared_ptr<DBusResponse<>> response, int error) {
+ auto report_success =
+ base::BindOnce([](std::shared_ptr<DBusResponse<>> response) {
+ response->Return();
+ });
+ modem_control->RestoreActiveSlot(base::BindOnce(
+ &RunOnSuccess<std::shared_ptr<DBusResponse<>>>,
+ std::move(report_success), std::move(response)));
+ },
+ context_->modem_control(), response);
+ context_->modem_control()->ProcessEuiccEvent(
+ {physical_slot_, EuiccStep::END},
+ std::move(restore_slot_and_dbus_return));
});
}