File: search_file_scanner.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 (164 lines) | stat: -rw-r--r-- 6,094 bytes parent folder | download | duplicates (3)
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
// Copyright 2024 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/app_list/search/search_file_scanner.h"

#include <algorithm>
#include <map>
#include <string_view>
#include <utility>

#include "ash/constants/ash_pref_names.h"
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_map.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/profiles/profile.h"
#include "components/prefs/pref_service.h"
#include "ui/base/metadata/base_type_conversion.h"

namespace app_list {

namespace {

// The minimum time in days required to wait after the previous file scan
// logging. Assuming that the user local file won't change dramatically, log at
// most once per week to minimize the power and performance impact.
constexpr int kMinFileScanDelayDays = 7;

// The delay before we start the file scan when the file is constructed.
constexpr base::TimeDelta kFileScanDelay = base::Minutes(10);

// The key for total file numbers in `extension_file_counts`.
constexpr std::string_view kTotal = "Total";

// The list of file extensions we are interested in.
// It is in the form of {"extension format", "extension name"}, with the format
// to match the file extension and the name to get the uma metric name.
// Note:
//    When adding new extension to this map, also updates the
//    `SearchFileExtension` variants in
//    `tools/metrics/histograms/metadata/apps/histograms.xml`.
constexpr auto kExtensionMap =
    base::MakeFixedFlatMap<std::string_view, std::string_view>({
        // Image extensions.
        {".png", "Png"},
        {".jpg", "Jpg"},
        {".jpeg", "Jpeg"},
        {".webp", "Webp"},
    });

base::Time GetLastScanLogTime(Profile* profile) {
  return profile->GetPrefs()->GetTime(
      ash::prefs::kLauncherSearchLastFileScanLogTime);
}

void SetLastScanLogTime(Profile* profile, const base::Time& time) {
  profile->GetPrefs()->SetTime(ash::prefs::kLauncherSearchLastFileScanLogTime,
                               time);
}

// Looks up and counts the total file number and the file numbers of each
// extension that is of interest in the `search_path` and excludes the ones that
// overlaps with `trash_paths`.
//
// This function can be executed on any thread other than the UI thread, while
// the `SearchFileScanner` is created on UI thread.
void FullScan(const base::FilePath& search_path,
              const std::vector<base::FilePath>& trash_paths) {
  base::Time start_time = base::Time::Now();
  base::FileEnumerator file_enumerator(search_path, /*recursive=*/true,
                                       base::FileEnumerator::FILES);

  std::map<std::string_view, int> extension_file_counts;
  for (base::FilePath file_path = file_enumerator.Next(); !file_path.empty();
       file_path = file_enumerator.Next()) {
    // Exclude any paths that are parented at an enabled trash location.
    if (std::ranges::any_of(trash_paths,
                            [&file_path](const base::FilePath& trash_path) {
                              return trash_path.IsParent(file_path);
                            })) {
      continue;
    }
    // Always counts the total file number.
    extension_file_counts[kTotal]++;

    // Increments the extension count if the extension of the current file is of
    // interest.
    const auto extension_lookup =
        kExtensionMap.find(base::ToLowerASCII(file_path.Extension()));
    if (extension_lookup != kExtensionMap.end()) {
      extension_file_counts[extension_lookup->second]++;
    }
  }

  // Logs execution time.
  base::UmaHistogramTimes("Apps.AppList.SearchFileScan.ExecutionTime",
                          base::Time::Now() - start_time);

  // Logs file extension count data.
  for (const auto& it : extension_file_counts) {
    base::UmaHistogramCounts100000(
        base::StrCat({"Apps.AppList.SearchFileScan.", it.first}), it.second);
  }
}

}  // namespace

SearchFileScanner::SearchFileScanner(
    Profile* profile,
    const base::FilePath& root_path,
    const std::vector<base::FilePath>& excluded_paths,
    std::optional<base::TimeDelta> start_delay_override)
    : profile_(profile),
      root_path_(root_path),
      excluded_paths_(excluded_paths) {
  // Early returns if file scan has been done recently.
  // TODO(b/337130427) considering replacing this with a timer, which can covers
  // the users which does not logout frequently.
  if (profile_ && base::Time::Now() - GetLastScanLogTime(profile_) <
                      base::Days(kMinFileScanDelayDays)) {
    return;
  }

  // Delays the file scan so that it does not add regressions to the user login.
  // Skips the delay in test.
  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(&SearchFileScanner::StartFileScan,
                     weak_factory_.GetWeakPtr()),
      start_delay_override.value_or(kFileScanDelay));
}

SearchFileScanner::~SearchFileScanner() = default;

void SearchFileScanner::StartFileScan() {
  // Do the file scan on a non-UI thread.
  base::ThreadPool::PostTaskAndReply(
      FROM_HERE,
      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
      base::BindOnce(&FullScan, std::move(root_path_),
                     std::move(excluded_paths_)),
      base::BindOnce(&SearchFileScanner::OnScanComplete,
                     weak_factory_.GetWeakPtr()));
}

void SearchFileScanner::OnScanComplete() {
  // Ensures the `profile_` is still alive to avoid any possible crashes during
  // shutdown.
  if (profile_) {
    SetLastScanLogTime(profile_, base::Time::Now());
  }
}

}  // namespace app_list