blob: 83f5a32061e339ec035631bf2f8c5aa2a77a5dec [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2022 The Chromium Authors
Ramin Halavati26dcce22022-02-23 13:11:142// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Abigail Klein7a63c572024-02-28 20:45:095#include "services/screen_ai/screen_ai_service_impl.h"
Ramin Halavati26dcce22022-02-23 13:11:146
Ramin Halavati55fdf4d2022-11-07 06:35:187#include <memory>
Ramin Halavatifd712d8e2022-10-19 05:11:278#include <utility>
Ramin Halavatib044504d2022-10-24 06:36:349#include <vector>
Ramin Halavatifd712d8e2022-10-19 05:11:2710
11#include "base/check.h"
Ramin Halavatiad969ef2024-05-21 06:59:4712#include "base/check_is_test.h"
Ramin Halavati55fdf4d2022-11-07 06:35:1813#include "base/functional/bind.h"
14#include "base/location.h"
Ramin Halavatifafff152022-12-05 09:39:3815#include "base/logging.h"
Kyungjun Lee9d755a02022-11-08 17:57:1116#include "base/metrics/histogram_functions.h"
Ramin Halavati5ff855c2022-04-27 16:30:4817#include "base/process/process.h"
Sean Maher5b9af51f2022-11-21 15:32:4718#include "base/task/single_thread_task_runner.h"
Ramin Halavati55fdf4d2022-11-07 06:35:1819#include "base/task/thread_pool.h"
Abigail Kleindfdde352023-01-27 21:03:1020#include "services/metrics/public/cpp/ukm_builders.h"
21#include "services/metrics/public/cpp/ukm_recorder.h"
Abigail Klein7a63c572024-02-28 20:45:0922#include "services/screen_ai/buildflags/buildflags.h"
23#include "services/screen_ai/proto/main_content_extractor_proto_convertor.h"
24#include "services/screen_ai/proto/visual_annotator_proto_convertor.h"
25#include "services/screen_ai/public/cpp/utilities.h"
Ramin Halavatieddadb62022-05-04 17:29:4926#include "ui/accessibility/accessibility_features.h"
Ramin Halavati4f904d0a2024-06-25 15:32:3227#include "ui/accessibility/ax_node.h"
28#include "ui/accessibility/ax_tree.h"
Ramin Halavati84231072022-08-17 08:02:2029#include "ui/accessibility/ax_tree_id.h"
Nektarios Paisiosf73d6972022-06-04 11:32:2530#include "ui/gfx/geometry/rect_f.h"
Ramin Halavati367352a2022-04-14 06:00:2331
Ramin Halavatia9b50a102024-02-07 18:37:2332#if BUILDFLAG(USE_FAKE_SCREEN_AI)
Abigail Klein7a63c572024-02-28 20:45:0933#include "services/screen_ai/screen_ai_library_wrapper_fake.h"
Ramin Halavati767b8fc2024-02-02 06:23:0834#else
Abigail Klein7a63c572024-02-28 20:45:0935#include "services/screen_ai/screen_ai_library_wrapper_impl.h"
Ramin Halavati767b8fc2024-02-02 06:23:0836#endif
37
Ramin Halavati69bf7752022-04-04 09:58:1538namespace screen_ai {
Ramin Halavati26dcce22022-02-23 13:11:1439
Ramin Halavati6d4fc2b2022-06-16 15:33:3540namespace {
41
Ramin Halavatiad969ef2024-05-21 06:59:4742// These values are persisted to logs. Entries should not be renumbered and
43// numeric values should never be reused.
44enum class OcrClientTypeForMetrics {
45 kTest = 0,
46 kPdfViewer = 1,
47 kLocalSearch = 2,
48 kCameraApp = 3,
49 kPdfSearchify = 4,
50 kMediaApp = 5,
51 kMaxValue = kMediaApp
52};
53
54OcrClientTypeForMetrics GetClientType(mojom::OcrClientType client_type) {
55 switch (client_type) {
56 case mojom::OcrClientType::kTest:
57 CHECK_IS_TEST();
58 return OcrClientTypeForMetrics::kTest;
59 case mojom::OcrClientType::kPdfViewer:
60 return OcrClientTypeForMetrics::kPdfViewer;
61 case mojom::OcrClientType::kLocalSearch:
62 return OcrClientTypeForMetrics::kLocalSearch;
63 case mojom::OcrClientType::kCameraApp:
64 return OcrClientTypeForMetrics::kCameraApp;
65 case mojom::OcrClientType::kPdfSearchify:
66 return OcrClientTypeForMetrics::kPdfSearchify;
67 case mojom::OcrClientType::kMediaApp:
68 return OcrClientTypeForMetrics::kMediaApp;
69 }
70}
71
Ramin Halavati89a36c92023-05-05 15:01:0372ui::AXTreeUpdate ConvertVisualAnnotationToTreeUpdate(
Arthur Sonzognic571efb2024-01-26 20:26:1873 const std::optional<chrome_screen_ai::VisualAnnotation>& annotation_proto,
Ramin Halavati89a36c92023-05-05 15:01:0374 const gfx::Rect& image_rect) {
75 if (!annotation_proto) {
76 VLOG(0) << "Screen AI library could not process snapshot or no OCR data.";
77 return ui::AXTreeUpdate();
78 }
79
80 return VisualAnnotationToAXTreeUpdate(*annotation_proto, image_rect);
81}
82
Abigail Klein18673702024-03-05 20:59:0683ui::AXNodeID ComputeMainNode(
84 const ui::AXTree* tree,
85 const std::vector<ui::AXNodeID>& content_node_ids) {
86 ui::AXNode* front = tree->GetFromId(content_node_ids.front());
87 ui::AXNode* back = tree->GetFromId(content_node_ids.back());
88 ui::AXNode* main = front->GetLowestCommonAncestor(*back);
89 return main->id();
90}
91
Ramin Halavati26ae6b72022-11-10 06:45:0592} // namespace
93
Ramin Halavati39bab0d2024-01-30 06:05:0194// The library accepts simple pointers to model data retrieval functions, hence
95// callback functions with linked object are not safe to pass.
96// Since library initialization functions are called in a single thread process,
97// we choose the active model data instance before calling the library
98// initializer and release it when initialization is completed.
99PreloadedModelData* g_active_model_data_instance = nullptr;
Ramin Halavati8ced6392023-09-25 07:27:50100
101// Keeps the content of model files, and replies to calls for copying them.
102class PreloadedModelData {
103 public:
104 PreloadedModelData(const PreloadedModelData&) = delete;
105 PreloadedModelData& operator=(const PreloadedModelData&) = delete;
Ramin Halavati39bab0d2024-01-30 06:05:01106 ~PreloadedModelData() { CHECK_NE(g_active_model_data_instance, this); }
Ramin Halavati8ced6392023-09-25 07:27:50107
108 static std::unique_ptr<PreloadedModelData> Create(
Ramin Halavatifbc62022023-11-10 11:05:07109 base::flat_map<base::FilePath, base::File> model_files) {
Ramin Halavati8ced6392023-09-25 07:27:50110 return base::WrapUnique<PreloadedModelData>(
111 new PreloadedModelData(std::move(model_files)));
112 }
113
114 // Returns 0 if file is not found.
115 static uint32_t GetDataSize(const char* relative_file_path) {
Ramin Halavati39bab0d2024-01-30 06:05:01116 CHECK(g_active_model_data_instance);
117 return base::Contains(g_active_model_data_instance->data_,
Ramin Halavati8ced6392023-09-25 07:27:50118 relative_file_path)
Ramin Halavati39bab0d2024-01-30 06:05:01119 ? g_active_model_data_instance->data_[relative_file_path].size()
Ramin Halavati8ced6392023-09-25 07:27:50120 : 0;
121 }
122
123 // Assumes that `buffer` has enough size.
124 static void CopyData(const char* relative_file_path,
125 uint32_t buffer_size,
126 char* buffer) {
Ramin Halavati39bab0d2024-01-30 06:05:01127 CHECK(g_active_model_data_instance);
128 CHECK(base::Contains(g_active_model_data_instance->data_,
Ramin Halavati8ced6392023-09-25 07:27:50129 relative_file_path));
130 const std::vector<char>& data =
Ramin Halavati39bab0d2024-01-30 06:05:01131 g_active_model_data_instance->data_[relative_file_path];
Ramin Halavati8ced6392023-09-25 07:27:50132 CHECK_GE(buffer_size, data.size());
133 memcpy(buffer, data.data(), data.size());
134 }
135
Ramin Halavati39bab0d2024-01-30 06:05:01136 void SetAsActive(bool assign) {
137 if (assign) {
138 g_active_model_data_instance = this;
139 } else {
140 g_active_model_data_instance = nullptr;
141 }
142 }
Ramin Halavati8ced6392023-09-25 07:27:50143
144 private:
145 explicit PreloadedModelData(
Ramin Halavatifbc62022023-11-10 11:05:07146 base::flat_map<base::FilePath, base::File> model_files) {
Ramin Halavati8ced6392023-09-25 07:27:50147 for (auto& model_file : model_files) {
148 std::vector<char> buffer;
149 int64_t length = model_file.second.GetLength();
150 if (length < 0) {
151 VLOG(0) << "Could not query Screen AI model file's length: "
152 << model_file.first;
153 continue;
154 }
155
156 buffer.resize(length);
157 if (model_file.second.Read(0, buffer.data(), length) != length) {
158 VLOG(0) << "Could not read Screen AI model file's content: "
159 << model_file.first;
160 continue;
161 }
Ramin Halavatifbc62022023-11-10 11:05:07162 data_[model_file.first.MaybeAsASCII()] = std::move(buffer);
Ramin Halavati8ced6392023-09-25 07:27:50163 }
164 }
Ramin Halavati39bab0d2024-01-30 06:05:01165
166 std::map<std::string, std::vector<char>> data_;
Ramin Halavati8ced6392023-09-25 07:27:50167};
168
Ramin Halavati26ae6b72022-11-10 06:45:05169ScreenAIService::ScreenAIService(
Ramin Halavatic1e4fa92023-05-17 17:22:06170 mojo::PendingReceiver<mojom::ScreenAIServiceFactory> receiver)
171 : factory_receiver_(this, std::move(receiver)),
172 ocr_receiver_(this),
Ramin Halavatiad969ef2024-05-21 06:59:47173 main_content_extraction_receiver_(this) {
174 screen_ai_annotators_.set_disconnect_handler(base::BindRepeating(
175 &ScreenAIService::ReceiverDisconnected, weak_ptr_factory_.GetWeakPtr()));
176}
Ramin Halavati26ae6b72022-11-10 06:45:05177
178ScreenAIService::~ScreenAIService() = default;
179
Ramin Halavati8ced6392023-09-25 07:27:50180void ScreenAIService::LoadLibrary(const base::FilePath& library_path) {
Abigail Klein7a63c572024-02-28 20:45:09181 // The ScopedBlockingCall in LoadLibrary guarantees that this is not run on
182 // the UI thread.
Ramin Halavatia9b50a102024-02-07 18:37:23183#if BUILDFLAG(USE_FAKE_SCREEN_AI)
Ramin Halavati767b8fc2024-02-02 06:23:08184 library_ = std::make_unique<ScreenAILibraryWrapperFake>();
185#else
186 library_ = std::make_unique<ScreenAILibraryWrapperImpl>();
187#endif
Ramin Halavati8ced6392023-09-25 07:27:50188
189 bool load_sucessful = library_->Load(library_path);
190 base::UmaHistogramBoolean("Accessibility.ScreenAI.Library.Initialized",
191 load_sucessful);
192
193 if (!load_sucessful) {
194 library_.reset();
195 return;
196 }
197
198 uint32_t version_major;
199 uint32_t version_minor;
200 library_->GetLibraryVersion(version_major, version_minor);
201 VLOG(2) << "Screen AI library version: " << version_major << "."
202 << version_minor;
203
204#if BUILDFLAG(IS_CHROMEOS_ASH)
205 library_->SetLogger();
206#endif
207
208 if (features::IsScreenAIDebugModeEnabled()) {
209 library_->EnableDebugMode();
210 }
211
212 library_->SetFileContentFunctions(&PreloadedModelData::GetDataSize,
213 &PreloadedModelData::CopyData);
214}
215
Ramin Halavatic1e4fa92023-05-17 17:22:06216void ScreenAIService::InitializeMainContentExtraction(
Ramin Halavatid139a0f2023-02-28 13:25:28217 const base::FilePath& library_path,
Ramin Halavatifbc62022023-11-10 11:05:07218 base::flat_map<base::FilePath, base::File> model_files,
Ramin Halavatic1e4fa92023-05-17 17:22:06219 mojo::PendingReceiver<mojom::MainContentExtractionService>
220 main_content_extractor_service_receiver,
221 InitializeMainContentExtractionCallback callback) {
222 if (!library_) {
Ramin Halavati8ced6392023-09-25 07:27:50223 LoadLibrary(library_path);
Ramin Halavatic1e4fa92023-05-17 17:22:06224 }
225
226 if (!library_) {
227 std::move(callback).Run(false);
228 base::Process::TerminateCurrentProcessImmediately(-1);
229 }
230
Ramin Halavati26ae6b72022-11-10 06:45:05231 base::ThreadPool::PostTaskAndReplyWithResult(
232 FROM_HERE,
233 {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
Ramin Halavati8ced6392023-09-25 07:27:50234 base::BindOnce(&PreloadedModelData::Create, std::move(model_files)),
Ramin Halavatic1e4fa92023-05-17 17:22:06235 base::BindOnce(&ScreenAIService::InitializeMainContentExtractionInternal,
236 weak_ptr_factory_.GetWeakPtr(),
237 std::move(main_content_extractor_service_receiver),
238 std::move(callback)));
Ramin Halavatib044504d2022-10-24 06:36:34239}
240
Ramin Halavatic1e4fa92023-05-17 17:22:06241void ScreenAIService::InitializeMainContentExtractionInternal(
242 mojo::PendingReceiver<mojom::MainContentExtractionService>
243 main_content_extractor_service_receiver,
244 InitializeMainContentExtractionCallback callback,
Ramin Halavati8ced6392023-09-25 07:27:50245 std::unique_ptr<PreloadedModelData> model_data) {
Ramin Halavati0e2ae5742023-11-10 05:57:54246 // `model_data` contains the content of the model files and its accessors are
247 // passed to the library. It should be kept in memory until after library
248 // initialization.
Ramin Halavati39bab0d2024-01-30 06:05:01249 model_data->SetAsActive(true);
Ramin Halavati8ced6392023-09-25 07:27:50250 bool init_successful = library_->InitMainContentExtraction();
Ramin Halavati39bab0d2024-01-30 06:05:01251 model_data->SetAsActive(false);
Ramin Halavati1d57c2d2023-05-24 05:18:24252 base::UmaHistogramBoolean(
253 "Accessibility.ScreenAI.MainContentExtraction.Initialized",
254 init_successful);
255 if (!init_successful) {
Ramin Halavatic1e4fa92023-05-17 17:22:06256 std::move(callback).Run(false);
Ramin Halavati89a36c92023-05-05 15:01:03257 return;
Ramin Halavatid139a0f2023-02-28 13:25:28258 }
Ramin Halavati89a36c92023-05-05 15:01:03259
Ramin Halavatic1e4fa92023-05-17 17:22:06260 // This interface should be created only once.
261 CHECK(!main_content_extraction_receiver_.is_bound());
262
263 main_content_extraction_receiver_.Bind(
264 std::move(main_content_extractor_service_receiver));
265
266 std::move(callback).Run(true);
267}
268
269void ScreenAIService::InitializeOCR(
270 const base::FilePath& library_path,
Ramin Halavatifbc62022023-11-10 11:05:07271 base::flat_map<base::FilePath, base::File> model_files,
Ramin Halavatic1e4fa92023-05-17 17:22:06272 mojo::PendingReceiver<mojom::OCRService> ocr_service_receiver,
273 InitializeOCRCallback callback) {
274 if (!library_) {
Ramin Halavati8ced6392023-09-25 07:27:50275 LoadLibrary(library_path);
Ramin Halavatic1e4fa92023-05-17 17:22:06276 }
277
278 if (!library_) {
279 std::move(callback).Run(false);
280 base::Process::TerminateCurrentProcessImmediately(-1);
281 }
Ramin Halavati0e2ae5742023-11-10 05:57:54282 base::ThreadPool::PostTaskAndReplyWithResult(
283 FROM_HERE,
284 {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
285 base::BindOnce(&PreloadedModelData::Create, std::move(model_files)),
286 base::BindOnce(&ScreenAIService::InitializeOCRInternal,
287 weak_ptr_factory_.GetWeakPtr(),
288 std::move(ocr_service_receiver), std::move(callback)));
289}
Ramin Halavatic1e4fa92023-05-17 17:22:06290
Ramin Halavati0e2ae5742023-11-10 05:57:54291void ScreenAIService::InitializeOCRInternal(
292 mojo::PendingReceiver<mojom::OCRService> ocr_service_receiver,
Kyungjun Lee8acce9b2024-01-03 16:19:02293 InitializeOCRCallback callback,
Ramin Halavati0e2ae5742023-11-10 05:57:54294 std::unique_ptr<PreloadedModelData> model_data) {
295 // `model_data` contains the content of the model files and its accessors are
296 // passed to the library. It should be kept in memory until after library
297 // initialization.
Ramin Halavati39bab0d2024-01-30 06:05:01298 model_data->SetAsActive(true);
Ramin Halavati0e2ae5742023-11-10 05:57:54299 bool init_successful = library_->InitOCR();
Ramin Halavati39bab0d2024-01-30 06:05:01300 model_data->SetAsActive(false);
301
Ramin Halavati35d129f2023-06-22 16:37:09302 base::UmaHistogramBoolean("Accessibility.ScreenAI.OCR.Initialized",
Ramin Halavati1d57c2d2023-05-24 05:18:24303 init_successful);
Ramin Halavati480584332023-08-02 06:38:40304
Ramin Halavati1d57c2d2023-05-24 05:18:24305 if (!init_successful) {
Ramin Halavatic1e4fa92023-05-17 17:22:06306 std::move(callback).Run(false);
307 return;
308 }
309
310 // This interface should be created only once.
311 CHECK(!ocr_receiver_.is_bound());
312
313 ocr_receiver_.Bind(std::move(ocr_service_receiver));
314
315 std::move(callback).Run(true);
Ramin Halavatib044504d2022-10-24 06:36:34316}
Ramin Halavati26dcce22022-02-23 13:11:14317
318void ScreenAIService::BindAnnotator(
319 mojo::PendingReceiver<mojom::ScreenAIAnnotator> annotator) {
320 screen_ai_annotators_.Add(this, std::move(annotator));
321}
322
Ramin Halavatieddadb62022-05-04 17:29:49323void ScreenAIService::BindMainContentExtractor(
324 mojo::PendingReceiver<mojom::Screen2xMainContentExtractor>
325 main_content_extractor) {
326 screen_2x_main_content_extractors_.Add(this,
327 std::move(main_content_extractor));
328}
329
Arthur Sonzognic571efb2024-01-26 20:26:18330std::optional<chrome_screen_ai::VisualAnnotation>
Ramin Halavati2650ebe2023-11-27 19:56:35331ScreenAIService::PerformOcrAndRecordMetrics(const SkBitmap& image,
332 bool a11y_tree_request) {
Ramin Halavatiad969ef2024-05-21 06:59:47333 auto entry = ocr_client_types_.find(screen_ai_annotators_.current_receiver());
334 CHECK(entry != ocr_client_types_.end()) << "OCR client type is not set.";
335 base::UmaHistogramEnumeration("Accessibility.ScreenAI.OCR.ClientType",
336 GetClientType(entry->second));
337
Ramin Halavati7252cd02023-05-10 07:21:33338 base::TimeTicks start_time = base::TimeTicks::Now();
339 auto result = library_->PerformOcr(image);
340 base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time;
Ramin Halavati2650ebe2023-11-27 19:56:35341 int lines_count = result ? result->lines_size() : 0;
Ramin Halavati3ae33ca2024-01-01 12:58:39342 unsigned image_size = image.width() * image.height();
Ramin Halavati2650ebe2023-11-27 19:56:35343 VLOG(1) << "OCR returned " << lines_count << " lines in " << elapsed_time;
Ramin Halavati7252cd02023-05-10 07:21:33344
Ramin Halavati81b6d922024-06-07 14:20:34345 base::UmaHistogramBoolean("Accessibility.ScreenAI.OCR.Successful",
346 result.has_value());
Ramin Halavati2650ebe2023-11-27 19:56:35347 base::UmaHistogramCounts100("Accessibility.ScreenAI.OCR.LinesCount",
348 lines_count);
Ramin Halavatia0548b262023-06-27 05:56:47349 base::UmaHistogramCounts10M("Accessibility.ScreenAI.OCR.ImageSize10M",
Ramin Halavati3ae33ca2024-01-01 12:58:39350 image_size);
351 if (image_size < 500 * 500) {
352 base::UmaHistogramTimes("Accessibility.ScreenAI.OCR.Latency.Small",
353 elapsed_time);
354 } else if (image_size < 1000 * 1000) {
355 base::UmaHistogramTimes("Accessibility.ScreenAI.OCR.Latency.Medium",
356 elapsed_time);
357 } else if (image_size < 2000 * 2000) {
358 base::UmaHistogramTimes("Accessibility.ScreenAI.OCR.Latency.Large",
359 elapsed_time);
360 } else {
361 base::UmaHistogramTimes("Accessibility.ScreenAI.OCR.Latency.XLarge",
362 elapsed_time);
363 }
Ramin Halavati2650ebe2023-11-27 19:56:35364
365 // If needed to extend to more clients, an identifier can be passed from the
366 // client to introduce itself and these metrics can be collected based on it.
367 if (a11y_tree_request) {
368 base::UmaHistogramCounts100("Accessibility.ScreenAI.OCR.LinesCount.PDF",
369 lines_count);
370 base::UmaHistogramTimes("Accessibility.ScreenAI.OCR.Time.PDF",
371 elapsed_time);
372 base::UmaHistogramCounts10M("Accessibility.ScreenAI.OCR.ImageSize.PDF",
373 image.width() * image.height());
374 }
375
Ramin Halavati7252cd02023-05-10 07:21:33376 return result;
377}
378
Ramin Halavatiad969ef2024-05-21 06:59:47379void ScreenAIService::SetClientType(mojom::OcrClientType client_type) {
380 ocr_client_types_[screen_ai_annotators_.current_receiver()] = client_type;
381}
382
Dmitry Grebenyukc49b95ee2023-05-02 05:52:07383void ScreenAIService::PerformOcrAndReturnAnnotation(
384 const SkBitmap& image,
385 PerformOcrAndReturnAnnotationCallback callback) {
Arthur Sonzognic571efb2024-01-26 20:26:18386 std::optional<chrome_screen_ai::VisualAnnotation> annotation_proto =
Ramin Halavati2650ebe2023-11-27 19:56:35387 PerformOcrAndRecordMetrics(image, /*a11y_tree_request=*/false);
Dmitry Grebenyukc49b95ee2023-05-02 05:52:07388
Ramin Halavati89a36c92023-05-05 15:01:03389 if (annotation_proto) {
390 std::move(callback).Run(ConvertProtoToVisualAnnotation(*annotation_proto));
391 return;
Dmitry Grebenyukc49b95ee2023-05-02 05:52:07392 }
Ramin Halavati89a36c92023-05-05 15:01:03393
394 std::move(callback).Run(mojom::VisualAnnotation::New());
Dmitry Grebenyukc49b95ee2023-05-02 05:52:07395}
396
Kyungjun Lee90461522023-04-25 06:50:09397void ScreenAIService::PerformOcrAndReturnAXTreeUpdate(
Ramin Halavatib446c022023-03-29 15:36:36398 const SkBitmap& image,
Kyungjun Lee90461522023-04-25 06:50:09399 PerformOcrAndReturnAXTreeUpdateCallback callback) {
Arthur Sonzognic571efb2024-01-26 20:26:18400 std::optional<chrome_screen_ai::VisualAnnotation> annotation_proto =
Ramin Halavati2650ebe2023-11-27 19:56:35401 PerformOcrAndRecordMetrics(image, /*a11y_tree_request=*/true);
Ramin Halavati89a36c92023-05-05 15:01:03402 ui::AXTreeUpdate update = ConvertVisualAnnotationToTreeUpdate(
403 annotation_proto, gfx::Rect(image.width(), image.height()));
Kyungjun Lee90461522023-04-25 06:50:09404
Ramin Halavati89a36c92023-05-05 15:01:03405 // The original caller is always replied to, and an empty AXTreeUpdate tells
406 // that the annotation function was not successful.
407 std::move(callback).Run(update);
Ramin Halavatib446c022023-03-29 15:36:36408}
409
410void ScreenAIService::ExtractMainContent(const ui::AXTreeUpdate& snapshot,
411 ukm::SourceId ukm_source_id,
412 ExtractMainContentCallback callback) {
Abigail Klein18673702024-03-05 20:59:06413 base::TimeTicks start_time = base::TimeTicks::Now();
414 ui::AXTree tree;
415 std::optional<std::vector<int32_t>> content_node_ids;
416 bool success = ExtractMainContentInternal(snapshot, tree, content_node_ids);
417 base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time;
418 RecordMetrics(ukm_source_id, ukm::UkmRecorder::Get(), elapsed_time, success);
419
420 if (success) {
421 std::move(callback).Run(*content_node_ids);
422 } else {
423 std::move(callback).Run(std::vector<int32_t>());
424 }
425}
426
427void ScreenAIService::ExtractMainNode(const ui::AXTreeUpdate& snapshot,
428 ExtractMainNodeCallback callback) {
429 ui::AXTree tree;
430 std::optional<std::vector<int32_t>> content_node_ids;
431 bool success = ExtractMainContentInternal(snapshot, tree, content_node_ids);
432
433 if (success) {
434 ui::AXNodeID main_node_id = ComputeMainNode(&tree, *content_node_ids);
435 std::move(callback).Run(main_node_id);
436 } else {
437 std::move(callback).Run(ui::kInvalidAXNodeID);
438 }
439}
440
441bool ScreenAIService::ExtractMainContentInternal(
442 const ui::AXTreeUpdate& snapshot,
443 ui::AXTree& tree,
444 std::optional<std::vector<int32_t>>& content_node_ids) {
Ramin Halavatib446c022023-03-29 15:36:36445 // Early return if input is empty.
446 if (snapshot.nodes.empty()) {
Abigail Klein18673702024-03-05 20:59:06447 return false;
Ramin Halavatib33fc0c2022-05-06 09:32:22448 }
Ramin Halavatie2549a92022-08-02 07:43:19449
Abigail Klein18673702024-03-05 20:59:06450 // Deserialize the snapshot and reserialize it to a view hierarchy proto.
451 CHECK(tree.Unserialize(snapshot));
452 std::string serialized_snapshot = SnapshotToViewHierarchy(&tree);
453 content_node_ids = library_->ExtractMainContent(serialized_snapshot);
Ramin Halavati81b6d922024-06-07 14:20:34454 base::UmaHistogramBoolean(
455 "Accessibility.ScreenAI.MainContentExtraction.Successful",
456 content_node_ids.has_value());
Abigail Klein18673702024-03-05 20:59:06457 if (content_node_ids.has_value() && content_node_ids->size() > 0) {
Ramin Halavati5aad701d2023-05-09 17:23:29458 VLOG(2) << "Screen2x returned " << content_node_ids->size() << " node ids.";
Abigail Klein18673702024-03-05 20:59:06459 return true;
460 } else {
461 VLOG(0) << "Screen2x returned no results.";
462 return false;
Ramin Halavati5aad701d2023-05-09 17:23:29463 }
Abigail Klein18673702024-03-05 20:59:06464}
Ramin Halavati89a36c92023-05-05 15:01:03465
Abigail Klein18673702024-03-05 20:59:06466ui::AXNodeID ScreenAIService::ComputeMainNodeForTesting(
467 const ui::AXTree* tree,
468 const std::vector<ui::AXNodeID>& content_node_ids) {
469 return ComputeMainNode(tree, content_node_ids);
Abigail Kleindfdde352023-01-27 21:03:10470}
471
472// static
473void ScreenAIService::RecordMetrics(ukm::SourceId ukm_source_id,
474 ukm::UkmRecorder* ukm_recorder,
475 base::TimeDelta elapsed_time,
476 bool success) {
477 if (success) {
478 base::UmaHistogramTimes(
479 "Accessibility.ScreenAI.Screen2xDistillationTime.Success",
480 elapsed_time);
481 if (ukm_source_id != ukm::kInvalidSourceId) {
482 ukm::builders::Accessibility_ScreenAI(ukm_source_id)
483 .SetScreen2xDistillationTime_Success(elapsed_time.InMilliseconds())
484 .Record(ukm_recorder);
485 }
486 } else {
487 base::UmaHistogramTimes(
488 "Accessibility.ScreenAI.Screen2xDistillationTime.Failure",
489 elapsed_time);
490 if (ukm_source_id != ukm::kInvalidSourceId) {
491 ukm::builders::Accessibility_ScreenAI(ukm_source_id)
492 .SetScreen2xDistillationTime_Failure(elapsed_time.InMilliseconds())
493 .Record(ukm_recorder);
494 }
495 }
Ramin Halavatieddadb62022-05-04 17:29:49496}
497
Ramin Halavatiad969ef2024-05-21 06:59:47498void ScreenAIService::ReceiverDisconnected() {
499 auto entry = ocr_client_types_.find(screen_ai_annotators_.current_receiver());
500 if (entry != ocr_client_types_.end()) {
501 ocr_client_types_.erase(entry);
502 }
503}
504
Ramin Halavati26dcce22022-02-23 13:11:14505} // namespace screen_ai