esim_profile.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/services/cellular_setup/esim_profile.h"
  5. #include "ash/constants/ash_features.h"
  6. #include "ash/services/cellular_setup/esim_manager.h"
  7. #include "ash/services/cellular_setup/esim_mojo_utils.h"
  8. #include "ash/services/cellular_setup/euicc.h"
  9. #include "ash/services/cellular_setup/public/mojom/esim_manager.mojom-shared.h"
  10. #include "ash/services/cellular_setup/public/mojom/esim_manager.mojom.h"
  11. #include "base/metrics/histogram_functions.h"
  12. #include "base/metrics/histogram_macros.h"
  13. #include "base/strings/utf_string_conversions.h"
  14. #include "chromeos/ash/components/dbus/hermes/hermes_euicc_client.h"
  15. #include "chromeos/ash/components/dbus/hermes/hermes_profile_client.h"
  16. #include "chromeos/ash/components/dbus/hermes/hermes_response_status.h"
  17. #include "chromeos/ash/components/network/cellular_connection_handler.h"
  18. #include "chromeos/ash/components/network/cellular_esim_profile.h"
  19. #include "chromeos/ash/components/network/cellular_esim_uninstall_handler.h"
  20. #include "chromeos/ash/components/network/cellular_inhibitor.h"
  21. #include "chromeos/ash/components/network/hermes_metrics_util.h"
  22. #include "chromeos/ash/components/network/network_connection_handler.h"
  23. #include "chromeos/ash/components/network/network_event_log.h"
  24. #include "chromeos/ash/components/network/network_handler.h"
  25. #include "chromeos/ash/components/network/network_state_handler.h"
  26. #include "components/device_event_log/device_event_log.h"
  27. #include "components/user_manager/user_manager.h"
  28. #include "dbus/object_path.h"
  29. namespace ash::cellular_setup {
  30. namespace {
  31. bool IsGuestModeActive() {
  32. return user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
  33. user_manager::UserManager::Get()->IsLoggedInAsPublicAccount();
  34. }
  35. bool IsESimProfilePropertiesEqualToState(
  36. const mojom::ESimProfilePropertiesPtr& properties,
  37. const CellularESimProfile& esim_profile_state) {
  38. return esim_profile_state.iccid() == properties->iccid &&
  39. esim_profile_state.name() == properties->name &&
  40. esim_profile_state.nickname() == properties->nickname &&
  41. esim_profile_state.service_provider() ==
  42. properties->service_provider &&
  43. ProfileStateToMojo(esim_profile_state.state()) == properties->state &&
  44. esim_profile_state.activation_code() == properties->activation_code;
  45. }
  46. // Measures the time from which this function is called to when |callback|
  47. // is expected to run. The measured time difference should capture the time it
  48. // took for a pending profile to be fully downloaded.
  49. ESimProfile::InstallProfileCallback CreateTimedInstallProfileCallback(
  50. ESimProfile::InstallProfileCallback callback) {
  51. return base::BindOnce(
  52. [](ESimProfile::InstallProfileCallback callback,
  53. base::Time installation_start_time,
  54. mojom::ProfileInstallResult result) -> void {
  55. std::move(callback).Run(result);
  56. if (result != mojom::ProfileInstallResult::kSuccess)
  57. return;
  58. UMA_HISTOGRAM_MEDIUM_TIMES(
  59. "Network.Cellular.ESim.ProfileDownload.PendingProfile.Latency",
  60. base::Time::Now() - installation_start_time);
  61. },
  62. std::move(callback), base::Time::Now());
  63. }
  64. } // namespace
  65. ESimProfile::ESimProfile(const CellularESimProfile& esim_profile_state,
  66. Euicc* euicc,
  67. ESimManager* esim_manager)
  68. : euicc_(euicc),
  69. esim_manager_(esim_manager),
  70. properties_(mojom::ESimProfileProperties::New()),
  71. path_(esim_profile_state.path()) {
  72. UpdateProperties(esim_profile_state, /*notify=*/false);
  73. properties_->eid = euicc->properties()->eid;
  74. }
  75. ESimProfile::~ESimProfile() {
  76. if (install_callback_) {
  77. NET_LOG(ERROR) << "Profile destroyed with unfulfilled install callback";
  78. }
  79. if (uninstall_callback_) {
  80. NET_LOG(ERROR) << "Profile destroyed with unfulfilled uninstall callbacks";
  81. }
  82. if (set_profile_nickname_callback_) {
  83. NET_LOG(ERROR)
  84. << "Profile destroyed with unfulfilled set profile nickname callbacks";
  85. }
  86. }
  87. void ESimProfile::GetProperties(GetPropertiesCallback callback) {
  88. std::move(callback).Run(properties_->Clone());
  89. }
  90. void ESimProfile::InstallProfile(const std::string& confirmation_code,
  91. InstallProfileCallback callback) {
  92. if (properties_->state == mojom::ProfileState::kInstalling ||
  93. properties_->state != mojom::ProfileState::kPending) {
  94. NET_LOG(ERROR) << "Profile is already installed or in installing state.";
  95. std::move(callback).Run(mojom::ProfileInstallResult::kFailure);
  96. return;
  97. }
  98. properties_->state = mojom::ProfileState::kInstalling;
  99. esim_manager_->NotifyESimProfileChanged(this);
  100. NET_LOG(USER) << "Installing profile with path " << path().value();
  101. install_callback_ = CreateTimedInstallProfileCallback(std::move(callback));
  102. EnsureProfileExistsOnEuiccCallback perform_install_profile_callback =
  103. base::BindOnce(&ESimProfile::PerformInstallProfile,
  104. weak_ptr_factory_.GetWeakPtr(), confirmation_code);
  105. esim_manager_->cellular_inhibitor()->InhibitCellularScanning(
  106. CellularInhibitor::InhibitReason::kInstallingProfile,
  107. base::BindOnce(&ESimProfile::EnsureProfileExistsOnEuicc,
  108. weak_ptr_factory_.GetWeakPtr(),
  109. std::move(perform_install_profile_callback)));
  110. }
  111. void ESimProfile::UninstallProfile(UninstallProfileCallback callback) {
  112. if (IsGuestModeActive()) {
  113. NET_LOG(ERROR) << "Cannot uninstall profile in guest mode.";
  114. std::move(callback).Run(mojom::ESimOperationResult::kFailure);
  115. return;
  116. }
  117. if (!IsProfileInstalled()) {
  118. NET_LOG(ERROR) << "Profile uninstall failed: Profile is not installed.";
  119. std::move(callback).Run(mojom::ESimOperationResult::kFailure);
  120. return;
  121. }
  122. if (IsProfileManaged()) {
  123. NET_LOG(ERROR)
  124. << "Profile uninstall failed: Cannot uninstall managed profile.";
  125. std::move(callback).Run(mojom::ESimOperationResult::kFailure);
  126. return;
  127. }
  128. NET_LOG(USER) << "Uninstalling profile with path " << path().value();
  129. uninstall_callback_ = base::BindOnce(
  130. [](UninstallProfileCallback callback,
  131. mojom::ESimOperationResult result) -> void {
  132. base::UmaHistogramBoolean(
  133. "Network.Cellular.ESim.ProfileUninstallationResult",
  134. result == mojom::ESimOperationResult::kSuccess);
  135. std::move(callback).Run(result);
  136. },
  137. std::move(callback));
  138. esim_manager_->cellular_esim_uninstall_handler()->UninstallESim(
  139. properties_->iccid, path_, euicc_->path(),
  140. base::BindOnce(&ESimProfile::OnProfileUninstallResult,
  141. weak_ptr_factory_.GetWeakPtr()));
  142. }
  143. void ESimProfile::SetProfileNickname(const std::u16string& nickname,
  144. SetProfileNicknameCallback callback) {
  145. if (IsGuestModeActive()) {
  146. NET_LOG(ERROR) << "Cannot rename profile in guest mode.";
  147. std::move(callback).Run(mojom::ESimOperationResult::kFailure);
  148. return;
  149. }
  150. if (IsProfileManaged()) {
  151. NET_LOG(ERROR) << "Cannot rename managed profile.";
  152. std::move(callback).Run(mojom::ESimOperationResult::kFailure);
  153. return;
  154. }
  155. if (set_profile_nickname_callback_) {
  156. NET_LOG(ERROR) << "Set Profile Nickname already in progress.";
  157. std::move(callback).Run(mojom::ESimOperationResult::kFailure);
  158. return;
  159. }
  160. if (properties_->state == mojom::ProfileState::kInstalling ||
  161. properties_->state == mojom::ProfileState::kPending) {
  162. NET_LOG(ERROR) << "Set Profile Nickname failed: Profile is not installed.";
  163. std::move(callback).Run(mojom::ESimOperationResult::kFailure);
  164. return;
  165. }
  166. NET_LOG(USER) << "Setting profile nickname for path " << path().value();
  167. set_profile_nickname_callback_ = base::BindOnce(
  168. [](SetProfileNicknameCallback callback,
  169. mojom::ESimOperationResult result) -> void {
  170. base::UmaHistogramBoolean(
  171. "Network.Cellular.ESim.ProfileRenameResult",
  172. result == mojom::ESimOperationResult::kSuccess);
  173. std::move(callback).Run(result);
  174. },
  175. std::move(callback));
  176. EnsureProfileExistsOnEuiccCallback perform_set_profile_nickname_callback =
  177. base::BindOnce(&ESimProfile::PerformSetProfileNickname,
  178. weak_ptr_factory_.GetWeakPtr(), nickname);
  179. esim_manager_->cellular_inhibitor()->InhibitCellularScanning(
  180. CellularInhibitor::InhibitReason::kRenamingProfile,
  181. base::BindOnce(&ESimProfile::EnsureProfileExistsOnEuicc,
  182. weak_ptr_factory_.GetWeakPtr(),
  183. std::move(perform_set_profile_nickname_callback)));
  184. }
  185. void ESimProfile::UpdateProperties(
  186. const CellularESimProfile& esim_profile_state,
  187. bool notify) {
  188. if (IsESimProfilePropertiesEqualToState(properties_, esim_profile_state)) {
  189. return;
  190. }
  191. properties_->iccid = esim_profile_state.iccid();
  192. properties_->name = esim_profile_state.name();
  193. properties_->nickname = esim_profile_state.nickname();
  194. properties_->service_provider = esim_profile_state.service_provider();
  195. properties_->state = ProfileStateToMojo(esim_profile_state.state());
  196. properties_->activation_code = esim_profile_state.activation_code();
  197. if (notify) {
  198. esim_manager_->NotifyESimProfileChanged(this);
  199. esim_manager_->NotifyESimProfileListChanged(euicc_);
  200. }
  201. }
  202. void ESimProfile::OnProfileRemove() {
  203. // Run pending callbacks before profile is removed.
  204. if (uninstall_callback_) {
  205. // This profile could be removed before UninstallHandler returns. Return a
  206. // success since the profile will be removed.
  207. std::move(uninstall_callback_).Run(mojom::ESimOperationResult::kSuccess);
  208. }
  209. // Installation or setting nickname could trigger a request for profiles. If
  210. // this profile gets removed at that point, return the pending call with
  211. // failure.
  212. if (install_callback_) {
  213. std::move(install_callback_).Run(mojom::ProfileInstallResult::kFailure);
  214. }
  215. if (set_profile_nickname_callback_) {
  216. std::move(set_profile_nickname_callback_)
  217. .Run(mojom::ESimOperationResult::kFailure);
  218. }
  219. }
  220. mojo::PendingRemote<mojom::ESimProfile> ESimProfile::CreateRemote() {
  221. mojo::PendingRemote<mojom::ESimProfile> esim_profile_remote;
  222. receiver_set_.Add(this, esim_profile_remote.InitWithNewPipeAndPassReceiver());
  223. return esim_profile_remote;
  224. }
  225. void ESimProfile::EnsureProfileExistsOnEuicc(
  226. EnsureProfileExistsOnEuiccCallback callback,
  227. std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
  228. if (!inhibit_lock) {
  229. NET_LOG(ERROR) << "Error inhibiting cellular device";
  230. std::move(callback).Run(/*request_profile_success=*/false,
  231. /*inhibit_lock=*/nullptr);
  232. return;
  233. }
  234. if (!ProfileExistsOnEuicc()) {
  235. if (IsProfileInstalled()) {
  236. esim_manager_->cellular_esim_profile_handler()->RefreshProfileList(
  237. euicc_->path(),
  238. base::BindOnce(&ESimProfile::OnRequestInstalledProfiles,
  239. weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
  240. std::move(inhibit_lock));
  241. } else {
  242. HermesEuiccClient::Get()->RequestPendingProfiles(
  243. euicc_->path(), /*root_smds=*/ESimManager::GetRootSmdsAddress(),
  244. base::BindOnce(&ESimProfile::OnRequestPendingProfiles,
  245. weak_ptr_factory_.GetWeakPtr(), std::move(callback),
  246. std::move(inhibit_lock)));
  247. }
  248. return;
  249. }
  250. std::move(callback).Run(/*request_profile_success=*/true,
  251. std::move(inhibit_lock));
  252. }
  253. void ESimProfile::OnRequestInstalledProfiles(
  254. EnsureProfileExistsOnEuiccCallback callback,
  255. std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
  256. bool success = inhibit_lock != nullptr;
  257. if (!success) {
  258. NET_LOG(ERROR) << "Error requesting installed profiles to ensure profile "
  259. << "exists on Euicc";
  260. }
  261. OnRequestProfiles(std::move(callback), std::move(inhibit_lock), success);
  262. }
  263. void ESimProfile::OnRequestPendingProfiles(
  264. EnsureProfileExistsOnEuiccCallback callback,
  265. std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
  266. HermesResponseStatus status) {
  267. bool success = status == HermesResponseStatus::kSuccess;
  268. if (!success) {
  269. NET_LOG(ERROR) << "Error requesting pending profiles to ensure profile "
  270. << "exists on Euicc; status: " << status;
  271. }
  272. OnRequestProfiles(std::move(callback), std::move(inhibit_lock), success);
  273. }
  274. void ESimProfile::OnRequestProfiles(
  275. EnsureProfileExistsOnEuiccCallback callback,
  276. std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
  277. bool success) {
  278. if (!success) {
  279. std::move(callback).Run(/*request_profile_success=*/false,
  280. std::move(inhibit_lock));
  281. return;
  282. }
  283. // If profile does not exist on Euicc even after request for profiles then
  284. // return failure. The profile was removed and this object will get destroyed
  285. // when CellularESimProfileHandler updates.
  286. if (!ProfileExistsOnEuicc()) {
  287. NET_LOG(ERROR) << "Unable to ensure profile exists on Euicc. path="
  288. << path_.value();
  289. std::move(callback).Run(/*request_profile_success=*/false,
  290. std::move(inhibit_lock));
  291. return;
  292. }
  293. std::move(callback).Run(/*request_profile_success=*/true,
  294. std::move(inhibit_lock));
  295. }
  296. void ESimProfile::PerformInstallProfile(
  297. const std::string& confirmation_code,
  298. bool request_profile_success,
  299. std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
  300. if (!request_profile_success) {
  301. properties_->state = mojom::ProfileState::kPending;
  302. esim_manager_->NotifyESimProfileChanged(this);
  303. std::move(install_callback_).Run(mojom::ProfileInstallResult::kFailure);
  304. return;
  305. }
  306. HermesEuiccClient::Get()->InstallPendingProfile(
  307. euicc_->path(), path_, confirmation_code,
  308. base::BindOnce(&ESimProfile::OnPendingProfileInstallResult,
  309. weak_ptr_factory_.GetWeakPtr(), std::move(inhibit_lock)));
  310. }
  311. void ESimProfile::PerformSetProfileNickname(
  312. const std::u16string& nickname,
  313. bool request_profile_success,
  314. std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
  315. if (!request_profile_success) {
  316. std::move(set_profile_nickname_callback_)
  317. .Run(mojom::ESimOperationResult::kFailure);
  318. return;
  319. }
  320. HermesProfileClient::Get()->RenameProfile(
  321. path_, base::UTF16ToUTF8(nickname),
  322. base::BindOnce(&ESimProfile::OnProfileNicknameSet,
  323. weak_ptr_factory_.GetWeakPtr(), std::move(inhibit_lock)));
  324. }
  325. void ESimProfile::OnPendingProfileInstallResult(
  326. std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
  327. HermesResponseStatus status) {
  328. hermes_metrics::LogInstallPendingProfileResult(status);
  329. if (status != HermesResponseStatus::kSuccess) {
  330. NET_LOG(ERROR) << "Error Installing pending profile status=" << status;
  331. properties_->state = mojom::ProfileState::kPending;
  332. esim_manager_->NotifyESimProfileChanged(this);
  333. std::move(install_callback_).Run(InstallResultFromStatus(status));
  334. return;
  335. }
  336. // inhibit_lock will be released by esim connection handler.
  337. // Cellular device will uninhibit automatically at that point.
  338. esim_manager_->cellular_connection_handler()
  339. ->PrepareNewlyInstalledCellularNetworkForConnection(
  340. euicc_->path(), path_, std::move(inhibit_lock),
  341. base::BindOnce(&ESimProfile::OnNewProfileEnableSuccess,
  342. weak_ptr_factory_.GetWeakPtr()),
  343. base::BindOnce(
  344. &ESimProfile::OnPrepareCellularNetworkForConnectionFailure,
  345. weak_ptr_factory_.GetWeakPtr()));
  346. }
  347. void ESimProfile::OnNewProfileEnableSuccess(const std::string& service_path) {
  348. const NetworkState* network_state =
  349. esim_manager_->network_state_handler()->GetNetworkState(service_path);
  350. if (!network_state) {
  351. OnPrepareCellularNetworkForConnectionFailure(
  352. service_path, NetworkConnectionHandler::kErrorNotFound);
  353. return;
  354. }
  355. if (!network_state->IsConnectingOrConnected()) {
  356. // The connection could fail but the user will be notified of connection
  357. // failures separately.
  358. esim_manager_->network_connection_handler()->ConnectToNetwork(
  359. service_path, /*success_callback=*/base::DoNothing(),
  360. /*error_callback=*/base::DoNothing(),
  361. /*check_error_state=*/false, ConnectCallbackMode::ON_STARTED);
  362. }
  363. DCHECK(install_callback_);
  364. std::move(install_callback_).Run(mojom::ProfileInstallResult::kSuccess);
  365. }
  366. void ESimProfile::OnPrepareCellularNetworkForConnectionFailure(
  367. const std::string& service_path,
  368. const std::string& error_name) {
  369. NET_LOG(ERROR) << "Error preparing network for connection. "
  370. << "Error: " << error_name
  371. << ", Service path: " << service_path;
  372. std::move(install_callback_).Run(mojom::ProfileInstallResult::kFailure);
  373. }
  374. void ESimProfile::OnProfileUninstallResult(bool success) {
  375. std::move(uninstall_callback_)
  376. .Run(success ? mojom::ESimOperationResult::kSuccess
  377. : mojom::ESimOperationResult::kFailure);
  378. }
  379. void ESimProfile::OnProfileNicknameSet(
  380. std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
  381. HermesResponseStatus status) {
  382. if (status != HermesResponseStatus::kSuccess) {
  383. NET_LOG(ERROR) << "ESimProfile rename error.";
  384. }
  385. std::move(set_profile_nickname_callback_)
  386. .Run(status == HermesResponseStatus::kSuccess
  387. ? mojom::ESimOperationResult::kSuccess
  388. : mojom::ESimOperationResult::kFailure);
  389. // inhibit_lock goes out of scope and will uninhibit automatically.
  390. }
  391. bool ESimProfile::ProfileExistsOnEuicc() {
  392. HermesEuiccClient::Properties* euicc_properties =
  393. HermesEuiccClient::Get()->GetProperties(euicc_->path());
  394. const std::vector<dbus::ObjectPath>& profile_paths =
  395. IsProfileInstalled()
  396. ? euicc_properties->installed_carrier_profiles().value()
  397. : euicc_properties->pending_carrier_profiles().value();
  398. auto iter = std::find(profile_paths.begin(), profile_paths.end(), path_);
  399. return iter != profile_paths.end();
  400. }
  401. bool ESimProfile::IsProfileInstalled() {
  402. return properties_->state != mojom::ProfileState::kPending &&
  403. properties_->state != mojom::ProfileState::kInstalling;
  404. }
  405. bool ESimProfile::IsProfileManaged() {
  406. NetworkStateHandler::NetworkStateList networks;
  407. esim_manager_->network_state_handler()->GetNetworkListByType(
  408. NetworkTypePattern::Cellular(),
  409. /*configure_only=*/false, /*visible=*/false, /*limit=*/0, &networks);
  410. for (const NetworkState* network : networks) {
  411. if (network->iccid() == properties_->iccid)
  412. return network->IsManagedByPolicy();
  413. }
  414. return false;
  415. }
  416. } // namespace ash::cellular_setup