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 
5 #include "chrome/browser/speech/speech_recognition_service.h"
6 
7 #include "chrome/browser/browser_process.h"
8 #include "chrome/browser/service_sandbox_type.h"
9 #include "chrome/common/pref_names.h"
10 #include "chrome/grit/generated_resources.h"
11 #include "components/prefs/pref_service.h"
12 #include "components/soda/constants.h"
13 #include "components/user_prefs/user_prefs.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/browser/service_process_host.h"
16 #include "content/public/browser/storage_partition.h"
17 #include "services/network/network_context.h"
18 #include "services/network/public/cpp/shared_url_loader_factory.h"
19 
20 namespace speech {
21 
22 constexpr base::TimeDelta kIdleProcessTimeout = base::TimeDelta::FromSeconds(5);
23 
SpeechRecognitionService(content::BrowserContext * context)24 SpeechRecognitionService::SpeechRecognitionService(
25     content::BrowserContext* context)
26     : context_(context),
27       enable_soda_(
28           base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption)) {}
29 
30 SpeechRecognitionService::~SpeechRecognitionService() = default;
31 
Create(mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> receiver)32 void SpeechRecognitionService::Create(
33     mojo::PendingReceiver<media::mojom::SpeechRecognitionContext> receiver) {
34   LaunchIfNotRunning();
35 
36   if (speech_recognition_service_.is_bound())
37     speech_recognition_service_->BindContext(std::move(receiver));
38 }
39 
OnNetworkServiceDisconnect()40 void SpeechRecognitionService::OnNetworkServiceDisconnect() {
41   if (!enable_soda_) {
42     // If the Speech On-Device API
43     // is not enabled, pass the URL
44     // loader factory to
45     // the speech recognition service to allow network requests to the Open
46     // Speech API.
47     mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
48     network::mojom::URLLoaderFactoryParamsPtr params =
49         network::mojom::URLLoaderFactoryParams::New();
50     params->process_id = network::mojom::kBrowserProcessId;
51     params->is_trusted = false;
52     params->automatically_assign_isolation_info = true;
53     network::mojom::NetworkContext* network_context =
54         content::BrowserContext::GetDefaultStoragePartition(context_)
55             ->GetNetworkContext();
56     network_context->CreateURLLoaderFactory(
57         url_loader_factory.InitWithNewPipeAndPassReceiver(), std::move(params));
58     speech_recognition_service_->SetUrlLoaderFactory(
59         std::move(url_loader_factory));
60   }
61 }
62 
LaunchIfNotRunning()63 void SpeechRecognitionService::LaunchIfNotRunning() {
64   if (speech_recognition_service_.is_bound())
65     return;
66 
67   PrefService* profile_prefs = user_prefs::UserPrefs::Get(context_);
68   PrefService* global_prefs = g_browser_process->local_state();
69   DCHECK(profile_prefs);
70   DCHECK(global_prefs);
71 
72   auto binary_path = global_prefs->GetFilePath(prefs::kSodaBinaryPath);
73   auto config_path = SpeechRecognitionService::GetSodaConfigPath(profile_prefs);
74   if (enable_soda_ && (binary_path.empty() || config_path.empty())) {
75     LOG(ERROR) << "Unable to find SODA files on the device.";
76     return;
77   }
78 
79   content::ServiceProcessHost::Launch(
80       speech_recognition_service_.BindNewPipeAndPassReceiver(),
81       content::ServiceProcessHost::Options()
82           .WithDisplayName(IDS_UTILITY_PROCESS_SPEECH_RECOGNITION_SERVICE_NAME)
83           .Pass());
84 
85   // Ensure that if the interface is ever disconnected (e.g. the service
86   // process crashes) or goes idle for a short period of time -- meaning there
87   // are no in-flight messages and no other interfaces bound through this
88   // one -- then we will reset |remote|, causing the service process to be
89   // terminated if it isn't already.
90   speech_recognition_service_.reset_on_disconnect();
91   speech_recognition_service_.reset_on_idle_timeout(kIdleProcessTimeout);
92 
93   speech_recognition_service_client_.reset();
94 
95   if (enable_soda_)
96     speech_recognition_service_->SetSodaPath(binary_path, config_path);
97 
98   speech_recognition_service_->BindSpeechRecognitionServiceClient(
99       speech_recognition_service_client_.BindNewPipeAndPassRemote());
100   OnNetworkServiceDisconnect();
101 }
102 
GetSodaConfigPath(PrefService * prefs)103 base::FilePath SpeechRecognitionService::GetSodaConfigPath(PrefService* prefs) {
104   speech::LanguageCode language = speech::GetLanguageCode(
105       prefs->GetString(prefs::kLiveCaptionLanguageCode));
106   PrefService* global_prefs = g_browser_process->local_state();
107   switch (language) {
108     case speech::LanguageCode::kNone:
109       NOTREACHED();
110       return base::FilePath();
111     case speech::LanguageCode::kEnUs:
112       return global_prefs->GetFilePath(prefs::kSodaEnUsConfigPath);
113     case speech::LanguageCode::kJaJp:
114       return global_prefs->GetFilePath(prefs::kSodaJaJpConfigPath);
115   }
116 
117   return base::FilePath();
118 }
119 }  // namespace speech
120