File: feedback_service.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; 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,806; 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 (669 lines) | stat: -rw-r--r-- 26,138 bytes parent folder | download | duplicates (2)
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
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/api/feedback_private/feedback_service.h"

#include <memory>
#include <string>
#include <utility>

#include "base/barrier_closure.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/statistics_recorder.h"
#include "base/strings/strcat.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "build/chromeos_buildflags.h"
#include "components/embedder_support/user_agent_utils.h"
#include "components/feedback/feedback_data.h"
#include "components/feedback/feedback_report.h"
#include "components/feedback/redaction_tool/redaction_tool.h"
#include "components/feedback/system_logs/system_logs_fetcher.h"
#include "components/feedback/system_logs/system_logs_source.h"
#include "components/variations/net/variations_command_line.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/api/feedback_private/feedback_private_delegate.h"
#include "extensions/browser/blob_reader.h"
#include "extensions/browser/extensions_browser_client.h"
#include "net/base/network_change_notifier.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "ash/public/cpp/assistant/controller/assistant_controller.h"
#include "base/base64.h"
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/storage_partition.h"
#include "extensions/browser/api/feedback_private/proto/hpke.pb.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/boringssl/src/include/openssl/hpke.h"
#include "third_party/cros_system_api/dbus/debugd/dbus-constants.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

#if !BUILDFLAG(IS_CHROMEOS)
#include "base/base64.h"
#include "base/feature_list.h"
#endif

namespace extensions {

using system_logs::SysLogsFetcherCallback;
using system_logs::SystemLogsFetcher;
using system_logs::SystemLogsResponse;

namespace {

#if !BUILDFLAG(IS_CHROMEOS)
constexpr char kVariationsStateAttachmentName[] = "variations_state.bin";
#endif

#if BUILDFLAG(IS_CHROMEOS)
// The paths are relative to "/var/log/" by default, which can be overwritten
// for testing purpose.
constexpr base::FilePath::CharType kBluetoothLogsFilePath[] =
    FILE_PATH_LITERAL("bluetooth/log.bz2");
constexpr base::FilePath::CharType kBluetoothLogsFilePathOld[] =
    FILE_PATH_LITERAL("bluetooth/log.bz2.old");
constexpr base::FilePath::CharType kBluetoothQualityReportFilePath[] =
    FILE_PATH_LITERAL("bluetooth/bluetooth_quality_report");

constexpr char kBluetoothLogsAttachmentName[] = "bluetooth_logs.bz2";
constexpr char kBluetoothLogsAttachmentNameOld[] = "bluetooth_logs.old.bz2";
constexpr char kBluetoothQualityReportAttachmentName[] =
    "bluetooth_quality_report";

constexpr char kVariationsAttachmentName[] = "variations.binary";
constexpr char kVariationsFetchHpkeKey[] =
    "https://www.gstatic.com/chromeos-feedback-variations-encryption-key/"
    "public_keyset.json";
constexpr int kVariationsMaxDownloadBytes = 512;

void AddAttachment(scoped_refptr<feedback::FeedbackData> feedback_data,
                   const base::FilePath& root_path,
                   const std::string& file_path,
                   const std::string& attachment_name) {
  std::string temp_log_content;
  if (base::ReadFileToString(root_path.Append(file_path), &temp_log_content)) {
    feedback_data->AddFile(attachment_name, std::move(temp_log_content));
  } else {
    LOG(WARNING) << "failed to add attachment " << attachment_name
                 << ": could not read file: " << file_path << " in "
                 << root_path.value();
  }
}

void AttachBluetoothLogs(scoped_refptr<feedback::FeedbackData> feedback_data,
                         const base::FilePath& root_path) {
  AddAttachment(feedback_data, root_path, kBluetoothLogsFilePath,
                kBluetoothLogsAttachmentName);
  AddAttachment(feedback_data, root_path, kBluetoothLogsFilePathOld,
                kBluetoothLogsAttachmentNameOld);
  AddAttachment(feedback_data, root_path, kBluetoothQualityReportFilePath,
                kBluetoothQualityReportAttachmentName);
}

// A new case must be added for every new log type. Otherwise the code should
// not compile.
std::string_view GetAttachmentName(debugd::FeedbackBinaryLogType log_type) {
  switch (log_type) {
    case debugd::WIFI_FIRMWARE_DUMP:
      return "wifi_firmware_dumps.tar.zst";
    case debugd::BLUETOOTH_FIRMWARE_DUMP:
      return "bluetooth_firmware_dumps.tar.zst";
  }
}
#endif

#if !BUILDFLAG(IS_CHROMEOS)
void IncludeVariations(scoped_refptr<feedback::FeedbackData> feedback_data) {
  std::vector<uint8_t> ciphertext;
  auto status =
      variations::VariationsCommandLine::GetForCurrentProcess().EncryptToString(
          &ciphertext);
  base::UmaHistogramEnumeration("Variations.VariationsStateEncryptionStatus",
                                status);
  // Variations is at best effort.
  if (status == variations::VariationsStateEncryptionStatus::kSuccess) {
    // This is a binary file.
    feedback_data->AddFile(kVariationsStateAttachmentName,
                           std::string(ciphertext.begin(), ciphertext.end()));
  }
}
#endif

void RedactFeedbackData(scoped_refptr<feedback::FeedbackData> feedback_data) {
  redaction::RedactionTool redactor(nullptr);
  redactor.EnableCreditCardRedaction(true);
  feedback_data->RedactDescription(redactor);
}

}  // namespace

FeedbackService::FeedbackService(content::BrowserContext* browser_context)
    : FeedbackService(
          browser_context,
          ExtensionsAPIClient::Get()->GetFeedbackPrivateDelegate()) {}

FeedbackService::FeedbackService(content::BrowserContext* browser_context,
                                 FeedbackPrivateDelegate* delegate)
    : browser_context_(browser_context), delegate_(delegate) {}

FeedbackService::~FeedbackService() = default;

void FeedbackService::RedactThenSendFeedback(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    SendFeedbackCallback callback) {
  base::ThreadPool::PostTaskAndReply(
      FROM_HERE, {base::TaskPriority::BEST_EFFORT},
      base::BindOnce(&RedactFeedbackData, feedback_data),
      base::BindOnce(&FeedbackService::SendFeedback, this, params,
                     feedback_data, std::move(callback)));
}

#if BUILDFLAG(IS_CHROMEOS)
void FeedbackService::SetLogFilesRootPathForTesting(
    const base::FilePath& log_file_root) {
  log_file_root_ = log_file_root;
}
#endif  // BUILDFLAG(IS_CHROMEOS)

// After the attached file and screenshot if available are fetched, the callback
// will be invoked. Other further processing will be done in background. The
// report will be sent out once all data are in place.
void FeedbackService::SendFeedback(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    SendFeedbackCallback callback) {
  auto* browser_client = ExtensionsBrowserClient::Get();
  feedback_data->set_locale(browser_client->GetApplicationLocale());
  feedback_data->set_user_agent(embedder_support::GetUserAgent());

  FetchAttachedFileAndScreenshot(
      feedback_data,
      base::BindOnce(&FeedbackService::OnAttachedFileAndScreenshotFetched, this,
                     params, feedback_data, std::move(callback)));
}

void FeedbackService::FetchAttachedFileAndScreenshot(
    scoped_refptr<feedback::FeedbackData> feedback_data,
    base::OnceClosure callback) {
  const bool must_attach_file = !feedback_data->attached_file_uuid().empty();
  const bool must_attach_screenshot = !feedback_data->screenshot_uuid().empty();
  auto barrier_closure = base::BarrierClosure(
      (must_attach_file ? 1 : 0) + (must_attach_screenshot ? 1 : 0),
      std::move(callback));

  if (must_attach_file) {
    auto populate_attached_file = base::BindOnce(
        [](scoped_refptr<feedback::FeedbackData> feedback_data,
           std::string data, int64_t /*length*/) {
          feedback_data->set_attached_file_uuid(std::string());
          feedback_data->AttachAndCompressFileData(std::move(data));
        },
        feedback_data);

    BlobReader::Read(
        browser_context_->GetBlobRemote(feedback_data->attached_file_uuid()),
        std::move(populate_attached_file).Then(barrier_closure));
  }

  if (must_attach_screenshot) {
    auto populate_screenshot = base::BindOnce(
        [](scoped_refptr<feedback::FeedbackData> feedback_data,
           std::string data, int64_t /*length*/) {
          feedback_data->set_screenshot_uuid(std::string());
          feedback_data->set_image(std::move(data));
        },
        feedback_data);
    BlobReader::Read(
        browser_context_->GetBlobRemote(feedback_data->screenshot_uuid()),
        std::move(populate_screenshot).Then(barrier_closure));
  }
}

void FeedbackService::OnAttachedFileAndScreenshotFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    SendFeedbackCallback callback) {
  if (params.load_system_info) {
    // The user has chosen to send system logs. They (and on ash more logs)
    // will be loaded in the background without blocking the client.
    FetchSystemInformation(params, feedback_data);
  } else {
#if BUILDFLAG(IS_CHROMEOS)
    if (feedback_data->sys_info()->size() > 0) {
      // The user has chosen to send system logs which has been loaded from the
      // client side. On ash, extra logs need to be fetched.
      FetchExtraLogs(params, feedback_data);
    } else {
      // The user has chosen not to send system logs.
      OnAllLogsFetched(params, feedback_data);
    }
#else
    OnAllLogsFetched(params, feedback_data);
#endif  // BUILDFLAG(IS_CHROMEOS)
  }

  base::UmaHistogramMediumTimes(
      "Feedback.Duration.FormSubmitToConfirmation",
      base::TimeTicks::Now() - params.form_submit_time);

  // True means report will be sent shortly.
  // False means report will be sent once the device is online.
  const bool status = !net::NetworkChangeNotifier::IsOffline();

  UMA_HISTOGRAM_BOOLEAN("Feedback.ReportSending.Online", status);

  // Notify client that data submitted has been received successfully. The
  // report will be sent out once further processing is done.
  std::move(callback).Run(status);
}

void FeedbackService::FetchSystemInformation(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data) {
  base::TimeTicks fetch_start_time = base::TimeTicks::Now();
  delegate_->FetchSystemInformation(
      browser_context_,
      base::BindOnce(&FeedbackService::OnSystemInformationFetched, this,
                     fetch_start_time, params, feedback_data));
}

void FeedbackService::OnSystemInformationFetched(
    base::TimeTicks fetch_start_time,
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    std::unique_ptr<system_logs::SystemLogsResponse> sys_info) {
  // Fetching is currently slow and could take up to 2 minutes on Chrome OS.
  base::UmaHistogramMediumTimes("Feedback.Duration.FetchSystemInformation",
                                base::TimeTicks::Now() - fetch_start_time);
  if (sys_info) {
    for (auto& itr : *sys_info) {
      if (FeedbackCommon::IncludeInSystemLogs(itr.first,
                                              params.is_internal_email))
        feedback_data->AddLog(std::move(itr.first), std::move(itr.second));
    }
  }
#if !BUILDFLAG(IS_CHROMEOS)
  if (base::FeatureList::IsEnabled(variations::kFeedbackIncludeVariations)) {
    IncludeVariations(feedback_data);
  }
#endif

#if BUILDFLAG(IS_CHROMEOS)
  FetchExtraLogs(params, feedback_data);
#else
  OnAllLogsFetched(params, feedback_data);
#endif  // BUILDFLAG(IS_CHROMEOS)
}

#if BUILDFLAG(IS_CHROMEOS)
void FeedbackService::FetchExtraLogs(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data) {
  delegate_->FetchExtraLogs(
      feedback_data,
      base::BindOnce(&FeedbackService::OnExtraLogsFetched, this, params));
}

void FeedbackService::OnExtraLogsFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data) {
  auto barrier_closure =
      base::BarrierClosure((params.send_bluetooth_logs ? 2 : 0) +
                               (params.send_wifi_debug_logs ? 1 : 0) + 1,
                           base::BindOnce(&FeedbackService::OnAllLogsFetched,
                                          this, params, feedback_data));

  EncryptVariations(feedback_data, barrier_closure);

  const user_manager::User* user =
      user_manager::UserManager::Get()->GetActiveUser();
  const auto account_identifier =
      cryptohome::CreateAccountIdentifierFromAccountId(
          user ? user->GetAccountId() : EmptyAccountId());

  // If bluetooth logs are requested, invoke AttachBluetoothLogs to add
  // them in a separate thread to avoid blocking the UI thread.
  if (params.send_bluetooth_logs) {
    base::ThreadPool::PostTaskAndReply(
        FROM_HERE, {base::MayBlock()},
        base::BindOnce(&AttachBluetoothLogs, feedback_data, log_file_root_),
        barrier_closure);

    binary_log_files_reader_.GetFeedbackBinaryLogs(
        account_identifier,
        debugd::FeedbackBinaryLogType::BLUETOOTH_FIRMWARE_DUMP,
        base::BindOnce(&FeedbackService::OnBinaryLogFilesFetched, this, params,
                       feedback_data, barrier_closure));
  }

  if (params.send_wifi_debug_logs) {
    binary_log_files_reader_.GetFeedbackBinaryLogs(
        account_identifier, debugd::FeedbackBinaryLogType::WIFI_FIRMWARE_DUMP,
        base::BindOnce(&FeedbackService::OnBinaryLogFilesFetched, this, params,
                       feedback_data, barrier_closure));
  }
}

void FeedbackService::OnBinaryLogFilesFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    base::RepeatingClosure barrier_closure_callback,
    feedback::BinaryLogFilesReader::BinaryLogsResponse binary_logs_response) {
  if (binary_logs_response) {
    for (auto& item : *binary_logs_response) {
      feedback_data->AddFile(GetAttachmentName(item.first).data(),
                             std::move(item.second));
    }
  }
  std::move(barrier_closure_callback).Run();
}
#endif  // BUILDFLAG(IS_CHROMEOS)

void FeedbackService::OnAllLogsFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data) {
  if (!params.send_tab_titles) {
    feedback_data->RemoveLog(
        feedback::FeedbackReport::kMemUsageWithTabTitlesKey);
  }
  feedback_data->CompressSystemInfo();

  if (params.send_histograms) {
    std::string histograms =
        base::StatisticsRecorder::ToJSON(base::JSON_VERBOSITY_LEVEL_FULL);
    feedback_data->SetAndCompressHistograms(std::move(histograms));
  }

  if (params.send_autofill_metadata) {
    feedback_data->CompressAutofillMetadata();
  }

  DCHECK(feedback_data->attached_file_uuid().empty());
  DCHECK(feedback_data->screenshot_uuid().empty());

#if BUILDFLAG(IS_CHROMEOS)
  // Send feedback to Assistant server if triggered from Google Assistant.
  if (feedback_data->from_assistant()) {
    ash::AssistantController::Get()->SendAssistantFeedback(
        feedback_data->assistant_debug_info_allowed(),
        feedback_data->description(), feedback_data->image());
  }
#endif

  // Signal the feedback object that the data from the feedback page has been
  // filled - the object will manage sending of the actual report.
  feedback_data->OnFeedbackPageDataComplete();
  base::UmaHistogramTimes("Feedback.Duration.FormSubmitToSendQueue",
                          base::TimeTicks::Now() - params.form_submit_time);
}

#if BUILDFLAG(IS_CHROMEOS)
void FeedbackService::EncryptVariations(
    scoped_refptr<feedback::FeedbackData> feedback_data,
    base::RepeatingClosure barrier_closure) {
  net::NetworkTrafficAnnotationTag traffic_annotation =
      net::DefineNetworkTrafficAnnotation(
          "chromeos_feedback_report_hpke_public_key_fetch_for_variations", R"(
        semantics {
          sender: "ChromeOS Feedback Report App"
          description:
            "Users can press Alt+Shift+i to report a bug or a feedback in "
            "general. Here we fetch a Hpke public key to encrypt "
            "the current running variations. This is ChromeOS-only."
          trigger:
            "When user chooses to send feedback to Google."
          data:
            "Fetches a HpKe public key. This key is used to encrypt the "
            "variations that are running at the time the feedback report was "
            "generated, and used by incident management engineering to triage "
            "issues potentially caused by experiments. "
            "If the user unchecks 'Send system information', this will "
            "not be fetched and variations will not be included in the "
            "feedback report."
          destination: GOOGLE_OWNED_SERVICE
          internal {
            contacts {
              email: "cros-feedback-app@google.com"
            }
          }
          user_data {
            type: NONE
          }
          last_reviewed: "2024-11-06"
        }
        policy {
          cookies_allowed: NO
          setting:
            "This feature cannot be disabled by settings and is only activated "
            "by direct user request."
          chrome_policy {
            UserFeedbackAllowed {
              UserFeedbackAllowed: false
            }
          }
        })");

  auto resource_request = std::make_unique<network::ResourceRequest>();
  resource_request->url = GURL(kVariationsFetchHpkeKey);
  resource_request->method = "GET";
  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;

  auto loader = network::SimpleURLLoader::Create(std::move(resource_request),
                                                 traffic_annotation);

  if (!url_loader_factory_) {
    url_loader_factory_ = browser_context_->GetDefaultStoragePartition()
                              ->GetURLLoaderFactoryForBrowserProcess();
  }

  // Loader will be owned by the callback, so we need a temporary reference to
  // avoid use after move.
  network::SimpleURLLoader* loader_ptr = loader.get();
  loader_ptr->DownloadToString(
      url_loader_factory_.get(),
      base::BindOnce(&FeedbackService::OnVariationsFetchHpkeURL, this,
                     std::move(loader), feedback_data, barrier_closure),
      kVariationsMaxDownloadBytes);
}

void FeedbackService::OnVariationsFetchHpkeURL(
    std::unique_ptr<network::SimpleURLLoader> loader,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    base::RepeatingClosure barrier_closure,
    std::unique_ptr<std::string> hpke_public_key) {
  if (!loader) {
    LOG(ERROR) << "invalid loader";
    return VariationsFinished(false, barrier_closure);
  }

  auto net_error = loader->NetError();
  int http_error = 0;
  if (loader->ResponseInfo() && loader->ResponseInfo()->headers) {
    http_error = loader->ResponseInfo()->headers->response_code();
  }
  if (!hpke_public_key || http_error != net::HTTP_OK) {
    LOG(ERROR) << "Unable to fetch hpke_public_key. http code: " << http_error
               << ", net error: " << net_error;
    return VariationsFinished(false, barrier_closure);
  }
  // Send the JSON string to a dedicated service for safe parsing.
  data_decoder_.ParseJson(
      *hpke_public_key,
      base::BindOnce(&FeedbackService::VariationsExtractHpkePublicKey, this,
                     feedback_data, barrier_closure));
}

// Sample JSON string:
// {
//   "primaryKeyId": 123,
//   "key": [
//     {
//       "keyData": {
//         "typeUrl": "type.googleapis.com/google.crypto.tink.HpkePublicKey",
//         "value": "Base64Encoded HPKE Proto",
//         "keyMaterialType": "ASYMMETRIC_PUBLIC"
//       },
//       "status": "ENABLED",
//       "keyId": 123,
//       "outputPrefixType": "RAW"
//     }
//   ]
// }
void FeedbackService::VariationsExtractHpkePublicKey(
    scoped_refptr<feedback::FeedbackData> feedback_data,
    base::RepeatingClosure barrier_closure,
    data_decoder::DataDecoder::ValueOrError result) {
  if (!result.has_value() || !result->is_dict()) {
    LOG(ERROR) << "Failed to parse JSON or it's not a dictionary.";
    return VariationsFinished(false, barrier_closure);
  }

  const base::Value::Dict& json_dict = result->GetDict();
  const base::Value::List* key_list = json_dict.FindList("key");

  if (!key_list || key_list->empty()) {
    LOG(ERROR) << "Key list not found or empty.";
    return VariationsFinished(false, barrier_closure);
  }

  // Get the first item in the "key" list
  const base::Value& key_item = (*key_list)[0];
  const base::Value::Dict* key_dict = key_item.GetIfDict();

  if (!key_dict) {
    LOG(ERROR) << "Unexpected format in 'key' item.";
    return VariationsFinished(false, barrier_closure);
  }

  // Extract "keyData" dictionary
  const base::Value::Dict* key_data_dict = key_dict->FindDict("keyData");
  if (!key_data_dict) {
    LOG(ERROR) << "Failed to find 'keyData' dictionary.";
    return VariationsFinished(false, barrier_closure);
  }

  // Extract "value" from "keyData"
  const std::string* base64_serialized_proto_hpke =
      key_data_dict->FindString("value");
  if (!base64_serialized_proto_hpke) {
    LOG(ERROR) << "Failed to extract 'value' from 'keyData'.";
    return VariationsFinished(false, barrier_closure);
  }

  // std::string base64_proto_key = *base64_proto_keyp;
  std::string serialized_proto_hpke;

  if (!base::Base64Decode(*base64_serialized_proto_hpke,
                          &serialized_proto_hpke)) {
    LOG(ERROR) << "base64 decode of hpke proto failed";
    return VariationsFinished(false, barrier_closure);
  }
  userfeedback::HpkePublicKey key_proto;
  if (!key_proto.ParseFromString(serialized_proto_hpke)) {
    LOG(ERROR) << "Failed to parse HpkePublicKey.";
    return VariationsFinished(false, barrier_closure);
  }

  std::string hpke_public_key_string = key_proto.public_key();
  std::vector<uint8_t> hpke_public_key;
  hpke_public_key.assign(hpke_public_key_string.begin(),
                         hpke_public_key_string.end());
  VLOG(1) << "HPKE public KEY:" << base::HexEncode(hpke_public_key);
  return VariationsEncryptWithHpkeKey(hpke_public_key, feedback_data,
                                      barrier_closure);
  ;
}

void FeedbackService::VariationsEncryptWithHpkeKey(
    const std::vector<uint8_t>& hpke_public_key,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    base::RepeatingClosure barrier_closure) {
  std::string variations_string =
      variations::VariationsCommandLine::GetForCurrentProcess().ToString();
  if (variations_string.empty()) {
    LOG(ERROR) << "Unable to get valid variations.";
    return VariationsFinished(false, barrier_closure);
  }
  std::vector<uint8_t> variations(variations_string.begin(),
                                  variations_string.end());
  bssl::ScopedEVP_HPKE_CTX sender_context;

  // This vector will hold the encapsulated shared secret "enc" followed by the
  // symmetrically encrypted ciphertext "ct". Start with a size big enough for
  // the shared secret.
  std::vector<uint8_t> encrypted_variations(EVP_HPKE_MAX_ENC_LENGTH);
  size_t encapsulated_shared_secret_len;

  if (!EVP_HPKE_CTX_setup_sender(
          /*ctx=*/sender_context.get(),
          /*out_enc=*/encrypted_variations.data(),
          /*out_enc_len=*/&encapsulated_shared_secret_len,
          /*max_enc=*/encrypted_variations.size(),
          /*kem=*/EVP_hpke_x25519_hkdf_sha256(),
          /*kdf=*/EVP_hpke_hkdf_sha256(),
          /*aead=*/EVP_hpke_aes_256_gcm(),
          /*peer_public_key=*/hpke_public_key.data(),
          /*peer_public_key_len=*/hpke_public_key.size(),
          /*info=*/nullptr,
          /*info_len=*/0)) {
    LOG(ERROR) << "hpke setup failed";
    return VariationsFinished(false, barrier_closure);
  }
  encrypted_variations.resize(encapsulated_shared_secret_len +
                              variations.size() +
                              EVP_HPKE_CTX_max_overhead(sender_context.get()));
  base::span<uint8_t> ciphertext =
      base::span(encrypted_variations).subspan(encapsulated_shared_secret_len);
  size_t ciphertext_len;

  if (!EVP_HPKE_CTX_seal(
          /*ctx=*/sender_context.get(),
          /*out=*/ciphertext.data(),
          /*out_len=*/&ciphertext_len,
          /*max_out_len=*/ciphertext.size(),
          /*in=*/variations.data(),
          /*in_len*/ variations.size(),
          /*ad=*/nullptr,
          /*ad_len=*/0)) {
    LOG(ERROR) << "hpke seal failed";
    return VariationsFinished(false, barrier_closure);
  }
  encrypted_variations.resize(encapsulated_shared_secret_len + ciphertext_len);
  feedback_data->AddFile(
      kVariationsAttachmentName,
      std::string(encrypted_variations.begin(), encrypted_variations.end()));
  return VariationsFinished(true, barrier_closure);
}

void FeedbackService::VariationsFinished(
    bool variations_attached,
    base::RepeatingClosure barrier_closure) {
  if (variations_attached) {
    VLOG(1) << "variations attached to feedback report";
  } else {
    VLOG(1) << "variations not attached to feedback report";
  }
  std::move(barrier_closure).Run();
}

#endif  // BUILDFLAG(IS_CHROMEOS)

}  // namespace extensions