File: apn_migrator.cc

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (610 lines) | stat: -rw-r--r-- 25,800 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/net/apn_migrator.h"

#include "ash/constants/ash_features.h"
#include "ash/public/cpp/network_config_service.h"
#include "base/containers/contains.h"
#include "base/values.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/network/managed_cellular_pref_handler.h"
#include "chromeos/ash/components/network/managed_network_configuration_handler.h"
#include "chromeos/ash/components/network/metrics/cellular_network_metrics_logger.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_metadata_store.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
#include "components/device_event_log/device_event_log.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash {

namespace {

using chromeos::network_config::mojom::ApnPropertiesPtr;
using chromeos::network_config::mojom::ApnState;
using chromeos::network_config::mojom::ApnType;
using chromeos::network_config::mojom::ManagedApnPropertiesPtr;

std::optional<ApnPropertiesPtr> GetPreRevampApnFromDict(
    const base::Value::Dict* cellular_dict,
    const char* key) {
  const base::Value::Dict* apn_dict =
      chromeos::network_config::GetDictionary(cellular_dict, key);
  if (!apn_dict) {
    return std::nullopt;
  }

  // Pre-revamp APNs with empty kAccessPointName will be ignored as they
  // indicate shill tried to send a NULL APN to modemmanager. If shill
  // uses a custom APN or modem DB APN, the kAccessPointName will be
  // non-empty.
  const std::string* access_point_name =
      apn_dict->FindString(::onc::cellular_apn::kAccessPointName);
  if (!access_point_name || access_point_name->empty()) {
    return std::nullopt;
  }

  return chromeos::network_config::GetApnProperties(
      *apn_dict,
      /*is_apn_revamp_enabled=*/false);
}

std::vector<ApnType> GetMigratedApnTypes(
    const ApnPropertiesPtr& pre_revamp_apn) {
  if (pre_revamp_apn->attach.has_value() &&
      !(*pre_revamp_apn->attach).empty()) {
    return {ApnType::kDefault, ApnType::kAttach};
  }
  return {ApnType::kDefault};
}

}  // namespace

ApnMigrator::ApnMigrator(
    ManagedCellularPrefHandler* managed_cellular_pref_handler,
    ManagedNetworkConfigurationHandler* network_configuration_handler,
    NetworkStateHandler* network_state_handler)
    : managed_cellular_pref_handler_(managed_cellular_pref_handler),
      network_configuration_handler_(network_configuration_handler),
      network_state_handler_(network_state_handler) {
  if (!NetworkHandler::IsInitialized()) {
    return;
  }
  // TODO(b/162365553): Only bind this lazily when CrosNetworkConfig is actually
  // used.
  ash::GetNetworkConfigService(
      remote_cros_network_config_.BindNewPipeAndPassReceiver());
  network_state_handler_observer_.Observe(network_state_handler_.get());
}

ApnMigrator::~ApnMigrator() = default;

bool ApnMigrator::has_iccids_changed(base::flat_set<std::string> new_iccids,
                                     base::flat_set<std::string> old_iccids) {
  if (new_iccids.size() != old_iccids.size()) {
    return true;
  }

  return !std::equal(new_iccids.begin(), new_iccids.end(), old_iccids.begin());
}

base::flat_set<std::string> ApnMigrator::extract_iccids(
    NetworkStateHandler::NetworkStateList& network_list) {
  base::flat_set<std::string> iccids;

  for (const NetworkState* network : network_list) {
    if (!network->iccid().empty()) {
      iccids.insert(network->iccid());
    }
  }

  return iccids;
}

void ApnMigrator::ResetOldIccidsForTesting() {
  old_iccids_.clear();
}

void ApnMigrator::NetworkListChanged() {
  NetworkStateHandler::NetworkStateList network_list;
  network_state_handler_->GetVisibleNetworkListByType(
      NetworkTypePattern::Cellular(), &network_list);
  base::flat_set<std::string> new_iccids = extract_iccids(network_list);

  if (has_iccids_changed(new_iccids, old_iccids_)) {
    old_iccids_ = new_iccids;

    for (const NetworkState* network : network_list) {
      // Only attempt to migrate networks known by Shill.
      if (network->IsNonShillCellularNetwork()) {
        continue;
      }

      if (network->iccid().empty()) {
        // If the network somehow has no iccid, don't attempt to migrate.
        NET_LOG(DEBUG) << "Network has no iccid, not migrating: "
                       << network->guid();
        continue;
      }

      // The network has already been updated in Shill with the correct logic
      // depending on if the flag is enabled or disabled. Finish early so we
      // don't redundantly update Shill.
      if (base::Contains(shill_updated_iccids_, network->iccid())) {
        continue;
      }

      bool has_network_been_migrated =
          managed_cellular_pref_handler_->ContainsApnMigratedIccid(
              network->iccid());
      if (!ash::features::IsApnRevampEnabled()) {
        // If the network has been marked as migrated, but the ApnRevamp flag is
        // disabled, the flag was disabled after being enabled. Clear
        // CustomApnList so that Shill knows to use legacy APN selection logic.
        if (has_network_been_migrated) {
          NET_LOG(EVENT) << "Network has been migrated but the revamp flag is "
                         << "disabled. Clearing CustomAPNList: "
                         << network->iccid();
          network_configuration_handler_->ClearShillProperties(
              network->path(), {shill::kCellularCustomApnListProperty},
              base::BindOnce(&ApnMigrator::OnClearPropertiesSuccess,
                             weak_factory_.GetWeakPtr(), network->iccid()),
              base::BindOnce(&ApnMigrator::OnClearPropertiesFailure,
                             weak_factory_.GetWeakPtr(), network->iccid(),
                             network->guid()));
        }
        continue;
      }

      if (!has_network_been_migrated) {
        NET_LOG(EVENT)
            << "Network has not been migrated, attempting to migrate: "
            << network->iccid();
        MigrateNetwork(*network);
        continue;
      }

      // The network has already been migrated, either the last time the flag
      // was on, or this time. Send Shill the revamp APN list.
      if (const base::Value::List* custom_apn_list =
              GetNetworkMetadataStore()->GetCustomApnList(network->guid())) {
        if (!ash::features::IsAllowApnModificationPolicyEnabled() ||
            network_configuration_handler_->AllowApnModification()) {
          NET_LOG(EVENT)
              << "Network has already been migrated, setting with the "
              << "populated custom APN list: " << network->iccid();
          SetShillCustomApnListForNetwork(*network, custom_apn_list);
        } else if (!network_configuration_handler_->AllowApnModification()) {
          NET_LOG(EVENT)
              << "Not setting custom APN list as admin has restricted "
                 "use of custom APNs";
          base::Value::List empty_custom_apn_list;
          SetShillCustomApnListForNetwork(*network, &empty_custom_apn_list);
        }
        continue;
      }

      NET_LOG(EVENT) << "Network has already been migrated, setting with the "
                     << "empty custom APN list: " << network->iccid();
      base::Value::List empty_custom_apn_list;
      SetShillCustomApnListForNetwork(*network, &empty_custom_apn_list);
    }
  }
}

void ApnMigrator::OnClearPropertiesSuccess(const std::string iccid) {
  NET_LOG(EVENT) << "Successfully cleared CustomAPNList for: " << iccid;
  shill_updated_iccids_.emplace(iccid);
}

void ApnMigrator::OnClearPropertiesFailure(const std::string iccid,
                                           const std::string guid,
                                           const std::string& error_name) {
  NET_LOG(ERROR) << "Failed to clear CustomAPNList for: " << iccid;
}

void ApnMigrator::SetShillCustomApnListForNetwork(
    const NetworkState& network,
    const base::Value::List* apn_list) {
  network_configuration_handler_->SetProperties(
      network.path(),
      chromeos::network_config::CustomApnListToOnc(network.guid(), apn_list),
      base::BindOnce(&ApnMigrator::OnSetShillCustomApnListSuccess,
                     weak_factory_.GetWeakPtr(), network.iccid()),
      base::BindOnce(&ApnMigrator::OnSetShillCustomApnListFailure,
                     weak_factory_.GetWeakPtr(), network.iccid(),
                     network.guid()));
}

void ApnMigrator::OnSetShillCustomApnListSuccess(const std::string iccid) {
  // Shill has successfully updated the network with the revamp APN list.
  shill_updated_iccids_.emplace(iccid);
  NET_LOG(EVENT) << "ApnMigrator: Update the custom APN "
                 << "list in Shill for network with ICCID: " << iccid;

  // The network has just been migrated.
  if (!managed_cellular_pref_handler_->ContainsApnMigratedIccid(iccid)) {
    CompleteMigrationAttempt(iccid, /*success=*/true);
  }
}

void ApnMigrator::OnSetShillCustomApnListFailure(
    const std::string iccid,
    const std::string guid,
    const std::string& error_name) {
  NET_LOG(ERROR) << "ApnMigrator: Failed to update the custom APN "
                 << "list in Shill for network: " << guid << ": [" << error_name
                 << ']';

  CompleteMigrationAttempt(iccid, /*success=*/false);
}

void ApnMigrator::MigrateNetwork(const NetworkState& network) {
  DCHECK(ash::features::IsApnRevampEnabled());

  // Return early if the network is already in the process of being migrated.
  if (base::Contains(iccids_in_migration_, network.iccid())) {
    NET_LOG(DEBUG) << "Attempting to migrate network that already has a "
                   << "migration in progress, returning early: "
                   << network.iccid();
    return;
  }

  DCHECK(!managed_cellular_pref_handler_->ContainsApnMigratedIccid(
      network.iccid()));

  // Get the pre-revamp APN list.
  const base::Value::List* custom_apn_list =
      GetNetworkMetadataStore()->GetPreRevampCustomApnList(network.guid());

  // If the pre-revamp APN list is empty, set the revamp list as empty and
  // finish the migration.
  if (!custom_apn_list || custom_apn_list->empty()) {
    NET_LOG(EVENT) << "Pre-revamp APN list is empty, sending empty list to "
                   << "Shill: " << network.iccid();
    base::Value::List empty_apn_list;
    SetShillCustomApnListForNetwork(network, &empty_apn_list);
    return;
  }

  // If the pre-revamp APN list is non-empty, get the network's managed
  // properties, to be used for the migration heuristic. This call is
  // asynchronous; mark the ICCID as migrating so that the network won't
  // be attempted to be migrated again while these properties are being fetched.
  iccids_in_migration_.emplace(network.iccid());

  NET_LOG(EVENT) << "Fetching managed properties for network: "
                 << network.iccid();
  network_configuration_handler_->GetManagedProperties(
      LoginState::Get()->primary_user_hash(), network.path(),
      base::BindOnce(&ApnMigrator::OnGetManagedProperties,
                     weak_factory_.GetWeakPtr(), network.iccid(),
                     network.guid()));
}

void ApnMigrator::OnGetManagedProperties(
    std::string iccid,
    std::string guid,
    const std::string& service_path,
    std::optional<base::Value::Dict> properties,
    std::optional<std::string> error) {
  if (error.has_value()) {
    NET_LOG(ERROR) << "Error fetching managed properties for " << iccid
                   << ", error: " << error.value();
    CompleteMigrationAttempt(iccid, /*success=*/false);
    return;
  }

  if (!properties) {
    NET_LOG(ERROR) << "Error fetching managed properties for " << iccid;
    CompleteMigrationAttempt(iccid, /*success=*/false);
    return;
  }

  const NetworkState* network =
      network_state_handler_->GetNetworkStateFromGuid(guid);
  if (!network) {
    NET_LOG(ERROR) << "Network no longer exists: " << guid;
    CompleteMigrationAttempt(iccid, /*success=*/false);
    return;
  }

  // Get the pre-revamp APN list.
  const base::Value::List* custom_apn_list =
      GetNetworkMetadataStore()->GetPreRevampCustomApnList(guid);

  // At this point, the pre-revamp APN list should not be empty. However, there
  // could be the case where the custom APN list was cleared during the
  // GetManagedProperties() call. If so, set the revamp list as empty and finish
  // the migration.
  if (!custom_apn_list || custom_apn_list->empty()) {
    NET_LOG(EVENT) << "Custom APN list cleared during GetManagedProperties() "
                   << "call, setting Shill with empty list for network: "
                   << guid;
    base::Value::List empty_apn_list;
    SetShillCustomApnListForNetwork(*network, &empty_apn_list);
    return;
  }

  ApnPropertiesPtr pre_revamp_custom_apn =
      chromeos::network_config::GetApnProperties(
          custom_apn_list->front().GetDict(),
          /*is_apn_revamp_enabled=*/false);

  NET_LOG(EVENT) << "pre_revamp_custom_apn: "
                 << pre_revamp_custom_apn->access_point_name;

  const base::Value::Dict* cellular_dict =
      chromeos::network_config::GetDictionary(&properties.value(),
                                              ::onc::network_config::kCellular);
  std::optional<ApnPropertiesPtr> last_connected_attach_apn =
      GetPreRevampApnFromDict(cellular_dict,
                              ::onc::cellular::kLastConnectedAttachApnProperty);
  NET_LOG(EVENT) << "last_connected_attach_apn: "
                 << (last_connected_attach_apn.has_value()
                         ? (*last_connected_attach_apn)->access_point_name
                         : "none");

  std::optional<ApnPropertiesPtr> last_connected_default_apn =
      GetPreRevampApnFromDict(
          cellular_dict, ::onc::cellular::kLastConnectedDefaultApnProperty);
  NET_LOG(EVENT) << "last_connected_default_apn: "
                 << (last_connected_default_apn.has_value()
                         ? (*last_connected_default_apn)->access_point_name
                         : "none");

  const bool is_network_managed = network->IsManagedByPolicy();
  if (is_network_managed && !last_connected_default_apn) {
    ManagedApnPropertiesPtr selected_apn =
        chromeos::network_config::GetManagedApnProperties(
            cellular_dict, ::onc::cellular::kAPN);
    if (selected_apn && pre_revamp_custom_apn->access_point_name ==
                            selected_apn->access_point_name->active_value) {
      NET_LOG(EVENT) << "Managed network's selected APN matches the saved "
                     << "custom APN, migrating APN: " << guid;
      // Ensure the APN is enabled when it's migrated so that it's attempted
      // to be used by the new UI.
      pre_revamp_custom_apn->state = ApnState::kEnabled;
      pre_revamp_custom_apn->apn_types =
          GetMigratedApnTypes(pre_revamp_custom_apn);
      CellularNetworkMetricsLogger::LogManagedCustomApnMigrationType(
          CellularNetworkMetricsLogger::ManagedApnMigrationType::
              kMatchesSelectedApn);
      CreateCustomApn(iccid, guid, std::move(pre_revamp_custom_apn));
    } else {
      NET_LOG(EVENT)
          << "Managed network's selected APN doesn't match the saved custom "
          << "APN, setting Shill with empty list for network: " << guid;
      base::Value::List empty_apn_list;
      CellularNetworkMetricsLogger::LogManagedCustomApnMigrationType(
          CellularNetworkMetricsLogger::ManagedApnMigrationType::
              kDoesNotMatchSelectedApn);
      SetShillCustomApnListForNetwork(*network, &empty_apn_list);
    }
    return;
  }

  NET_LOG(EVENT)
      << "Migrating network with non-managed flow, is network managed: "
      << is_network_managed;

  const bool has_last_connected_attach = last_connected_attach_apn.has_value();
  const bool is_last_connected_attach_custom =
      has_last_connected_attach &&
      pre_revamp_custom_apn->access_point_name ==
          (*last_connected_attach_apn)->access_point_name;

  const bool has_last_connected_default =
      last_connected_default_apn.has_value();
  const bool is_last_connected_default_custom =
      has_last_connected_default &&
      pre_revamp_custom_apn->access_point_name ==
          (*last_connected_default_apn)->access_point_name;

  if (is_last_connected_attach_custom && is_last_connected_default_custom) {
    NET_LOG(EVENT) << "Network's last_connected_default_apn and "
                      "last_connected_attach_apn match the "
                   << "saved custom APN, migrating APN: " << guid
                   << " in the Enabled state with Apn types Attach and Default";

    pre_revamp_custom_apn->state = ApnState::kEnabled;
    pre_revamp_custom_apn->apn_types = {ApnType::kAttach, ApnType::kDefault};
    CellularNetworkMetricsLogger::LogUnmanagedCustomApnMigrationType(
        CellularNetworkMetricsLogger::UnmanagedApnMigrationType::
            kMatchesLastConnectedAttachAndDefault);
    CreateCustomApn(iccid, guid, std::move(pre_revamp_custom_apn));
    return;
  }

  if (!has_last_connected_attach && !has_last_connected_default) {
    std::optional<ApnPropertiesPtr> last_good_apn =
        GetPreRevampApnFromDict(cellular_dict, ::onc::cellular::kLastGoodAPN);

    if (last_good_apn && pre_revamp_custom_apn->access_point_name ==
                             (*last_good_apn)->access_point_name) {
      NET_LOG(EVENT) << "Network's last good APN matches the saved "
                     << "custom APN, migrating APN: " << guid
                     << "in the Enabled state";
      // Ensure the APN is enabled when it's migrated so that it's
      // attempted to be used by the new UI.
      pre_revamp_custom_apn->state = ApnState::kEnabled;
      CellularNetworkMetricsLogger::LogUnmanagedCustomApnMigrationType(
          CellularNetworkMetricsLogger::UnmanagedApnMigrationType::
              kMatchesLastGoodApn);
    } else {
      NET_LOG(EVENT) << "Network's last good APN does not match the saved "
                     << "custom APN, migrating APN: " << guid
                     << "in the Disabled state";
      // The custom APN was last unsuccessful in connecting when the flag was
      // off. Preserve the details of the custom APN but with a state of
      // Disabled.
      pre_revamp_custom_apn->state = ApnState::kDisabled;
      CellularNetworkMetricsLogger::LogUnmanagedCustomApnMigrationType(
          CellularNetworkMetricsLogger::UnmanagedApnMigrationType::
              kDoesNotMatchLastGoodApn);
    }
    pre_revamp_custom_apn->apn_types =
        GetMigratedApnTypes(pre_revamp_custom_apn);
    CreateCustomApn(iccid, guid, std::move(pre_revamp_custom_apn));
    return;
  }

  if (!has_last_connected_attach && is_last_connected_default_custom) {
    NET_LOG(EVENT) << "Network has no last connected attach APN but has "
                   << "a last connected default APN that matches the "
                   << "saved custom APN, migrating APN: " << guid
                   << " in the Enabled state with Apn type Default";

    pre_revamp_custom_apn->state = ApnState::kEnabled;
    pre_revamp_custom_apn->apn_types = {ApnType::kDefault};

    CellularNetworkMetricsLogger::LogUnmanagedCustomApnMigrationType(
        CellularNetworkMetricsLogger::UnmanagedApnMigrationType::
            kMatchesLastConnectedDefaultNoLastConnectedAttach);
    CreateCustomApn(iccid, guid, std::move(pre_revamp_custom_apn));
    return;
  }

  if (has_last_connected_attach && is_last_connected_default_custom) {
    NET_LOG(EVENT)
        << "Only last_connected_default_apn matches "
           "pre_revamp_custom_apn, last_connected_attach_apn exists "
           "but does not match pre_revamp_custom_apn, migrating "
           "last_connected_default_apn APN in enabled state with "
           "Default and Attach type, and last_connected_attach_apn APN in "
           "enabled state with Attach type for network: "
        << guid;
    CellularNetworkMetricsLogger::LogUnmanagedCustomApnMigrationType(
        CellularNetworkMetricsLogger::UnmanagedApnMigrationType::
            kMatchesLastConnectedDefaultOnlyAndAttachExists);

    CreateDefaultThenAttachCustomApns(std::move(*last_connected_attach_apn),
                                      std::move(*last_connected_default_apn),
                                      /*can_use_default_apn_as_attach=*/true,
                                      guid, iccid);
    return;
  }

  if (has_last_connected_default && is_last_connected_attach_custom) {
    NET_LOG(EVENT) << "Only last_connected_attach_apn matches "
                      "pre_revamp_custom_apn, last_connected_default exists "
                      "but does not match pre_revamp_custom_apn";
    CellularNetworkMetricsLogger::LogUnmanagedCustomApnMigrationType(
        CellularNetworkMetricsLogger::UnmanagedApnMigrationType::
            kMatchesLastConnectedAttachOnlyAndDefaultExists);
    CreateDefaultThenAttachCustomApns(std::move(*last_connected_attach_apn),
                                      std::move(*last_connected_default_apn),
                                      /*can_use_default_apn_as_attach=*/false,
                                      guid, iccid);
    return;
  }

  NET_LOG(EVENT) << "Network's last connected default APN and attach APN "
                 << "do not match the saved custom APN, migrating APN: " << guid
                 << " in the Disabled state.";
  pre_revamp_custom_apn->state = ApnState::kDisabled;
  pre_revamp_custom_apn->apn_types = GetMigratedApnTypes(pre_revamp_custom_apn);

  CellularNetworkMetricsLogger::LogUnmanagedCustomApnMigrationType(
      CellularNetworkMetricsLogger::UnmanagedApnMigrationType::
          kNoMatchingConnectedApn);
  CreateCustomApn(iccid, guid, std::move(pre_revamp_custom_apn));
}

void ApnMigrator::CreateDefaultThenAttachCustomApns(
    chromeos::network_config::mojom::ApnPropertiesPtr attach_apn,
    chromeos::network_config::mojom::ApnPropertiesPtr default_apn,
    bool can_use_default_apn_as_attach,
    const std::string& guid,
    const std::string& iccid) {
  DCHECK(!attach_apn.is_null());
  DCHECK(!default_apn.is_null());
  NET_LOG(EVENT) << "Migrating default_apn: " << default_apn->access_point_name
                 << " in the enabled state; default_apn is also type attach: "
                 << can_use_default_apn_as_attach
                 << ". Then, migrate attach_apn: "
                 << attach_apn->access_point_name
                 << " in the enabled state for Network with guid: " << guid
                 << " and iccid: " << iccid;

  attach_apn->state = ApnState::kEnabled;
  attach_apn->apn_types = {ApnType::kAttach};

  default_apn->state = ApnState::kEnabled;
  if (can_use_default_apn_as_attach) {
    default_apn->apn_types = {ApnType::kAttach, ApnType::kDefault};
  } else {
    default_apn->apn_types = {ApnType::kDefault};
  }

  // Migrate default APN first, then attach APN in case both are to
  // be enabled; enabled default APN must be created before enabled attach APN.
  // See b/303565348.
  // Also, |CompleteMigrationAttempt()| should only be called
  // once, when both APNs have been migrated.
  auto create_custom_attach_apn_callback =
      base::BindOnce(&ApnMigrator::CreateCustomApn, weak_factory_.GetWeakPtr(),
                     iccid, guid, std::move(attach_apn), std::nullopt);

  auto on_create_default_apn_callback =
      base::BindOnce(
          [](base::OnceClosure success_callback, const std::string& guid,
             bool success) {
            if (success) {
              NET_LOG(EVENT)
                  << "ApnMigrator: Succeeded migrating custom default APN "
                     "for network guid in the enabled state: "
                  << guid
                  << ", now migrating different custom attach APN in enabled "
                     "state.";
              std::move(success_callback).Run();
              return;
            }
            NET_LOG(ERROR)
                << "ApnMigrator: Failed to create custom default APN for "
                   "network of guid: "
                << guid
                << ", so will not proceed to create associated custom attach "
                   "APN";
          },
          std::move(create_custom_attach_apn_callback), guid);

  CreateCustomApn(iccid, guid, std::move(default_apn),
                  std::move(on_create_default_apn_callback));
}

void ApnMigrator::CreateCustomApn(
    const std::string& iccid,
    const std::string& network_guid,
    chromeos::network_config::mojom::ApnPropertiesPtr apn,
    std::optional<base::OnceCallback<void(bool)>> success_callback) {
  remote_cros_network_config_->CreateCustomApn(
      network_guid, std::move(apn),
      success_callback ? std::move(*success_callback)
                       : base::BindOnce(&ApnMigrator::CompleteMigrationAttempt,
                                        weak_factory_.GetWeakPtr(), iccid));
}

void ApnMigrator::CompleteMigrationAttempt(const std::string& iccid,
                                           bool success) {
  iccids_in_migration_.erase(iccid);

  if (success) {
    NET_LOG(EVENT) << "ApnMigrator: Mark network with ICCID: " << iccid
                   << " as migrated";
    managed_cellular_pref_handler_->AddApnMigratedIccid(iccid);
  }
}

NetworkMetadataStore* ApnMigrator::GetNetworkMetadataStore() {
  if (network_metadata_store_for_testing_) {
    return network_metadata_store_for_testing_;
  }

  return NetworkHandler::Get()->network_metadata_store();
}

}  // namespace ash