SpeechRecognitionPrivate: Communicate the type of recognition to clients

This change plumbs the type of speech recognition used by
speechRecognitionPrivate to its clients by passing it through the
callback used when calling start(). Tests have been added at each layer
to confirm correct behavior.

The downstream change uses this information in Dictation to record
various metrics.

AX-Relnotes: N/A
Fixed: 1268591
Change-Id: Id64124d92ae570ae8ac9f1d9406952b5b7447b6b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3271772
Commit-Queue: Akihiro Ota <[email protected]>
Reviewed-by: Katie Dektar <[email protected]>
Reviewed-by: Evan Liu <[email protected]>
Reviewed-by: Devlin <[email protected]>
Cr-Commit-Position: refs/heads/main@{#941494}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6c09953..56b6302 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4814,6 +4814,8 @@
       "speech/network_speech_recognizer.h",
       "speech/on_device_speech_recognizer.cc",
       "speech/on_device_speech_recognizer.h",
+      "speech/speech_recognition_constants.cc",
+      "speech/speech_recognition_constants.h",
       "speech/speech_recognizer.cc",
       "speech/speech_recognizer.h",
       "speech/speech_recognizer_delegate.h",
@@ -7543,12 +7545,9 @@
       "sessions/tab_restore_service_load_waiter.h",
       "speech/fake_speech_recognition_service.cc",
       "speech/fake_speech_recognition_service.h",
-      "speech/speech_recognition_test_helper.cc",
-      "speech/speech_recognition_test_helper.h",
       "ui/tabs/tab_activity_simulator.cc",
       "ui/tabs/tab_activity_simulator.h",
     ]
-    deps += [ "//components/soda" ]
   }
 
   if (is_chromeos_ash) {
@@ -7597,6 +7596,8 @@
       "ash/policy/external_data/cloud_external_data_manager_base_test_util.h",
       "sharesheet/sharesheet_test_util.cc",
       "sharesheet/sharesheet_test_util.h",
+      "speech/speech_recognition_test_helper.cc",
+      "speech/speech_recognition_test_helper.h",
       "ui/app_list/test/chrome_app_list_test_support.cc",
       "ui/app_list/test/chrome_app_list_test_support.h",
       "ui/app_list/test/test_app_list_controller.cc",
@@ -7615,6 +7616,7 @@
       "//chromeos/disks",
       "//chromeos/login/auth",
       "//components/session_manager/core",
+      "//components/soda",
       "//components/webapk:proto",
       "//extensions/browser/api/messaging",
       "//ui/base:test_support",
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc
index 7652e29..7af1289c 100644
--- a/chrome/browser/ash/accessibility/dictation_browsertest.cc
+++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
 #include "chrome/browser/ash/input_method/textinput_test_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/speech/speech_recognition_constants.h"
 #include "chrome/browser/speech/speech_recognition_test_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -80,7 +81,7 @@
 // such as managing the speech recognition service.
 class DictationBaseTest
     : public InProcessBrowserTest,
-      public ::testing::WithParamInterface<SpeechRecognitionType> {
+      public ::testing::WithParamInterface<speech::SpeechRecognitionType> {
  protected:
   DictationBaseTest() : test_helper_(GetParam()) {}
   ~DictationBaseTest() override = default;
@@ -93,7 +94,7 @@
         test_helper_.GetEnabledFeatures();
     std::vector<base::Feature> disabled_features =
         test_helper_.GetDisabledFeatures();
-    if (GetParam() == SpeechRecognitionType::kOnDevice) {
+    if (GetParam() == speech::SpeechRecognitionType::kOnDevice) {
       enabled_features.push_back(
           ::features::kExperimentalAccessibilityDictationOffline);
     } else {
@@ -114,7 +115,7 @@
   }
 
   void TearDownOnMainThread() override {
-    if (GetParam() == SpeechRecognitionType::kNetwork)
+    if (GetParam() == speech::SpeechRecognitionType::kNetwork)
       content::SpeechRecognitionManager::SetManagerForTesting(nullptr);
 
     InProcessBrowserTest::TearDownOnMainThread();
@@ -171,7 +172,7 @@
   void EnableChromeVox() { GetManager()->EnableSpokenFeedback(true); }
 
   void SendSpeechResult(const std::string& result, bool is_final) {
-    if (GetParam() == SpeechRecognitionType::kNetwork && !is_final) {
+    if (GetParam() == speech::SpeechRecognitionType::kNetwork && !is_final) {
       // FakeSpeechRecognitionManager can only send final results,
       // so if this isn't final just send to Dictation directly.
       GetManager()->dictation_->OnSpeechResult(base::ASCIIToUTF16(result),
@@ -227,13 +228,15 @@
   ui::CompositionText empty_composition_text_;
 };
 
-INSTANTIATE_TEST_SUITE_P(Network,
-                         DictationTest,
-                         ::testing::Values(SpeechRecognitionType::kNetwork));
+INSTANTIATE_TEST_SUITE_P(
+    Network,
+    DictationTest,
+    ::testing::Values(speech::SpeechRecognitionType::kNetwork));
 
-INSTANTIATE_TEST_SUITE_P(OnDevice,
-                         DictationTest,
-                         ::testing::Values(SpeechRecognitionType::kOnDevice));
+INSTANTIATE_TEST_SUITE_P(
+    OnDevice,
+    DictationTest,
+    ::testing::Values(speech::SpeechRecognitionType::kOnDevice));
 
 IN_PROC_BROWSER_TEST_P(DictationTest, RecognitionEnds) {
   ToggleDictation();
@@ -441,7 +444,7 @@
     const std::string locale = it.first;
     bool works_offline = it.second.works_offline;
     bool installed = it.second.installed;
-    if (GetParam() == SpeechRecognitionType::kOnDevice &&
+    if (GetParam() == speech::SpeechRecognitionType::kOnDevice &&
         locale == speech::kUsEnglishLocale) {
       // Currently, the only locale supported by SODA is en-US. It should work
       // offline and be installed.
@@ -453,7 +456,7 @@
     }
   }
 
-  if (GetParam() == SpeechRecognitionType::kOnDevice) {
+  if (GetParam() == speech::SpeechRecognitionType::kOnDevice) {
     // Uninstall SODA and all language packs.
     speech::SodaInstaller::GetInstance()->UninstallSodaForTesting();
   } else {
@@ -579,13 +582,15 @@
   std::unique_ptr<ExtensionConsoleErrorObserver> console_observer_;
 };
 
-INSTANTIATE_TEST_SUITE_P(Network,
-                         DictationExtensionTest,
-                         ::testing::Values(SpeechRecognitionType::kNetwork));
+INSTANTIATE_TEST_SUITE_P(
+    Network,
+    DictationExtensionTest,
+    ::testing::Values(speech::SpeechRecognitionType::kNetwork));
 
-INSTANTIATE_TEST_SUITE_P(OnDevice,
-                         DictationExtensionTest,
-                         ::testing::Values(SpeechRecognitionType::kOnDevice));
+INSTANTIATE_TEST_SUITE_P(
+    OnDevice,
+    DictationExtensionTest,
+    ::testing::Values(speech::SpeechRecognitionType::kOnDevice));
 
 IN_PROC_BROWSER_TEST_P(DictationExtensionTest, StartsAndStopsRecognition) {
   ToggleDictationWithKeystroke();
@@ -744,13 +749,15 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-INSTANTIATE_TEST_SUITE_P(Network,
-                         DictationCommandsExtensionTest,
-                         ::testing::Values(SpeechRecognitionType::kNetwork));
+INSTANTIATE_TEST_SUITE_P(
+    Network,
+    DictationCommandsExtensionTest,
+    ::testing::Values(speech::SpeechRecognitionType::kNetwork));
 
-INSTANTIATE_TEST_SUITE_P(OnDevice,
-                         DictationCommandsExtensionTest,
-                         ::testing::Values(SpeechRecognitionType::kOnDevice));
+INSTANTIATE_TEST_SUITE_P(
+    OnDevice,
+    DictationCommandsExtensionTest,
+    ::testing::Values(speech::SpeechRecognitionType::kOnDevice));
 
 IN_PROC_BROWSER_TEST_P(DictationCommandsExtensionTest, TypesCommands) {
   std::string expected_text = "";
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.cc
index 0263678..8ef5cd2 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.h"
 
 #include "chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.h"
+#include "chrome/browser/speech/speech_recognition_constants.h"
 #include "chrome/common/extensions/api/speech_recognition_private.h"
 #include "content/public/browser/browser_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -43,13 +44,15 @@
 }
 
 void SpeechRecognitionPrivateStartFunction::OnStart(
+    speech::SpeechRecognitionType type,
     absl::optional<std::string> error) {
   if (error.has_value()) {
     Respond(Error(error.value()));
     return;
   }
 
-  Respond(NoArguments());
+  Respond(OneArgument(base::Value(api::speech_recognition_private::ToString(
+      speech::SpeechRecognitionTypeToApiType(type)))));
 }
 
 ExtensionFunction::ResponseAction SpeechRecognitionPrivateStopFunction::Run() {
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.h b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.h
index 587ea442..ef0ad5c7 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.h
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.h
@@ -8,6 +8,10 @@
 #include "extensions/browser/extension_function.h"
 #include "extensions/browser/extension_function_histogram_value.h"
 
+namespace speech {
+enum class SpeechRecognitionType;
+}  // namespace speech
+
 namespace extensions {
 
 // An API function that starts speech recognition.
@@ -22,7 +26,8 @@
 
  private:
   // A callback that is run when the speech recognition service starts.
-  void OnStart(absl::optional<std::string> error);
+  void OnStart(speech::SpeechRecognitionType type,
+               absl::optional<std::string> error);
 };
 
 // An API function that stops speech recognition.
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_apitest.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_apitest.cc
index c7a9308..61b9974 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_apitest.cc
@@ -28,19 +28,31 @@
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(Network,
-                         SpeechRecognitionPrivateApiTest,
-                         ::testing::Values(SpeechRecognitionType::kNetwork));
+INSTANTIATE_TEST_SUITE_P(
+    Network,
+    SpeechRecognitionPrivateApiTest,
+    ::testing::Values(speech::SpeechRecognitionType::kNetwork));
 
-INSTANTIATE_TEST_SUITE_P(OnDevice,
-                         SpeechRecognitionPrivateApiTest,
-                         ::testing::Values(SpeechRecognitionType::kOnDevice));
+INSTANTIATE_TEST_SUITE_P(
+    OnDevice,
+    SpeechRecognitionPrivateApiTest,
+    ::testing::Values(speech::SpeechRecognitionType::kOnDevice));
 
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateApiTest, Simple) {
   ASSERT_TRUE(RunExtensionTest("speech/speech_recognition_private/simple"))
       << message_;
 }
 
+// An end-to-end test that starts speech recognition and ensures that the
+// callback receives the correct boolean parameter for specifying on-device or
+// network speech.
+IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateApiTest, OnStart) {
+  SetCustomArg(api::speech_recognition_private::ToString(
+      speech::SpeechRecognitionTypeToApiType(GetParam())));
+  ASSERT_TRUE(RunExtensionTest("speech/speech_recognition_private/on_start"))
+      << message_;
+}
+
 // An end-to-end test that starts speech recognition, waits for results, then
 // finally stops recognition.
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateApiTest, StartResultStop) {
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.cc
index 38ad33c..9d52e29 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.cc
@@ -31,7 +31,7 @@
 }
 
 void SpeechRecognitionPrivateBaseTest::TearDownOnMainThread() {
-  if (GetParam() == SpeechRecognitionType::kNetwork)
+  if (GetParam() == speech::SpeechRecognitionType::kNetwork)
     content::SpeechRecognitionManager::SetManagerForTesting(nullptr);
 
   ExtensionApiTest::TearDownOnMainThread();
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h
index cffd5c1..0e3952b 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h
@@ -9,6 +9,7 @@
 
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/speech/speech_recognition_constants.h"
 #include "chrome/browser/speech/speech_recognition_test_helper.h"
 #include "content/public/test/browser_test.h"
 #include "extensions/common/features/feature_channel.h"
@@ -27,7 +28,7 @@
 // needs infrastructure from both this class and the ExtensionApiTest to run.
 class SpeechRecognitionPrivateBaseTest
     : public ExtensionApiTest,
-      public ::testing::WithParamInterface<SpeechRecognitionType> {
+      public ::testing::WithParamInterface<speech::SpeechRecognitionType> {
  protected:
   SpeechRecognitionPrivateBaseTest();
   ~SpeechRecognitionPrivateBaseTest() override;
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.cc
index 7705f375..61945e8 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.cc
@@ -132,13 +132,13 @@
     const std::string& key,
     absl::optional<std::string> locale,
     absl::optional<bool> interim_results,
-    ApiCallback callback) {
+    OnStartCallback callback) {
   GetSpeechRecognizer(key)->HandleStart(locale, interim_results,
                                         std::move(callback));
 }
 
 void SpeechRecognitionPrivateManager::HandleStop(const std::string& key,
-                                                 ApiCallback callback) {
+                                                 OnStopCallback callback) {
   GetSpeechRecognizer(key)->HandleStop(std::move(callback));
 }
 
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.h b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.h
index 7bf2e0a..3458bc14 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.h
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager.h
@@ -11,6 +11,7 @@
 
 #include "base/callback_forward.h"
 #include "chrome/browser/chromeos/extensions/speech/speech_recognition_private_delegate.h"
+#include "chrome/browser/speech/speech_recognition_constants.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -29,7 +30,10 @@
 class SpeechRecognitionPrivateManager
     : public KeyedService,
       public SpeechRecognitionPrivateDelegate {
-  using ApiCallback =
+  using OnStartCallback =
+      base::OnceCallback<void(speech::SpeechRecognitionType type,
+                              absl::optional<std::string> error)>;
+  using OnStopCallback =
       base::OnceCallback<void(absl::optional<std::string> error)>;
 
  public:
@@ -52,9 +56,9 @@
   void HandleStart(const std::string& key,
                    absl::optional<std::string> locale,
                    absl::optional<bool> interim_results,
-                   ApiCallback callback);
+                   OnStartCallback callback);
   // Handles a call to stop speech recognition.
-  void HandleStop(const std::string& key, ApiCallback callback);
+  void HandleStop(const std::string& key, OnStopCallback callback);
 
  private:
   friend class SpeechRecognitionPrivateManagerTest;
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager_browsertest.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager_browsertest.cc
index 39d3956..8e6450d 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_manager_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h"
 #include "chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h"
+#include "chrome/browser/speech/speech_recognition_constants.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 
@@ -45,7 +46,8 @@
       const std::string& key,
       absl::optional<std::string> locale,
       absl::optional<bool> interim_results,
-      base::OnceCallback<void(absl::optional<std::string>)> on_start_callback) {
+      base::OnceCallback<void(speech::SpeechRecognitionType,
+                              absl::optional<std::string>)> on_start_callback) {
     manager_->HandleStart(key, locale, interim_results,
                           std::move(on_start_callback));
   }
@@ -81,13 +83,15 @@
   SpeechRecognitionPrivateManager* manager_;
 };
 
-INSTANTIATE_TEST_SUITE_P(Network,
-                         SpeechRecognitionPrivateManagerTest,
-                         ::testing::Values(SpeechRecognitionType::kNetwork));
+INSTANTIATE_TEST_SUITE_P(
+    Network,
+    SpeechRecognitionPrivateManagerTest,
+    ::testing::Values(speech::SpeechRecognitionType::kNetwork));
 
-INSTANTIATE_TEST_SUITE_P(OnDevice,
-                         SpeechRecognitionPrivateManagerTest,
-                         ::testing::Values(SpeechRecognitionType::kOnDevice));
+INSTANTIATE_TEST_SUITE_P(
+    OnDevice,
+    SpeechRecognitionPrivateManagerTest,
+    ::testing::Values(speech::SpeechRecognitionType::kOnDevice));
 
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateManagerTest, CreateKey) {
   ASSERT_EQ("Testing", CreateKey("Testing", absl::optional<int>()));
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.cc
index 687ffe4..61dca200 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.cc
@@ -63,7 +63,8 @@
     }
   } else if (new_state == SPEECH_RECOGNIZER_RECOGNIZING) {
     DCHECK(!on_start_callback_.is_null());
-    std::move(on_start_callback_).Run(/*error=*/absl::optional<std::string>());
+    std::move(on_start_callback_)
+        .Run(/*type=*/type_, /*error=*/absl::optional<std::string>());
   } else if (new_state == SPEECH_RECOGNIZER_ERROR) {
     // When a speech recognition error occurs, ask the delegate to handle both
     // error and stop events.
@@ -78,9 +79,10 @@
 void SpeechRecognitionPrivateRecognizer::HandleStart(
     absl::optional<std::string> locale,
     absl::optional<bool> interim_results,
-    ApiCallback callback) {
+    OnStartCallback callback) {
   if (speech_recognizer_) {
     std::move(callback).Run(
+        /*type=*/type_,
         /*error=*/absl::optional<std::string>(kSpeechRecognitionStartError));
     RecognizerOff();
     return;
@@ -91,10 +93,12 @@
   // Choose which type of speech recognition, either on-device or network.
   Profile* profile = Profile::FromBrowserContext(context_);
   if (OnDeviceSpeechRecognizer::IsOnDeviceSpeechRecognizerAvailable(locale_)) {
+    type_ = speech::SpeechRecognitionType::kOnDevice;
     speech_recognizer_ = std::make_unique<OnDeviceSpeechRecognizer>(
         GetWeakPtr(), profile, locale_,
         /*recognition_mode_ime=*/true, /*enable_formatting=*/false);
   } else {
+    type_ = speech::SpeechRecognitionType::kNetwork;
     speech_recognizer_ = std::make_unique<NetworkSpeechRecognizer>(
         GetWeakPtr(),
         profile->GetDefaultStoragePartition()
@@ -104,7 +108,7 @@
   }
 }
 
-void SpeechRecognitionPrivateRecognizer::HandleStop(ApiCallback callback) {
+void SpeechRecognitionPrivateRecognizer::HandleStop(OnStopCallback callback) {
   if (current_state_ == SPEECH_RECOGNIZER_OFF) {
     // If speech recognition is already off, trigger the callback with an error
     // message.
@@ -130,7 +134,7 @@
 void SpeechRecognitionPrivateRecognizer::MaybeUpdateProperties(
     absl::optional<std::string> locale,
     absl::optional<bool> interim_results,
-    ApiCallback callback) {
+    OnStartCallback callback) {
   if (locale.has_value())
     locale_ = locale.value();
   if (interim_results.has_value())
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h
index e093861b..2d0b736 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h
@@ -9,6 +9,7 @@
 
 #include "base/callback_forward.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/speech/speech_recognition_constants.h"
 #include "chrome/browser/speech/speech_recognizer_delegate.h"
 #include "components/soda/constants.h"
 
@@ -27,7 +28,10 @@
 // events. It is also responsible for deciding whether to use the on-device or
 // network speech recognition.
 class SpeechRecognitionPrivateRecognizer : public SpeechRecognizerDelegate {
-  using ApiCallback =
+  using OnStartCallback =
+      base::OnceCallback<void(speech::SpeechRecognitionType type,
+                              absl::optional<std::string> error)>;
+  using OnStopCallback =
       base::OnceCallback<void(absl::optional<std::string> error)>;
 
  public:
@@ -48,10 +52,10 @@
   // Handles a call to start speech recognition.
   void HandleStart(absl::optional<std::string> locale,
                    absl::optional<bool> interim_results,
-                   ApiCallback callback);
+                   OnStartCallback callback);
   // Handles a call to stop speech recognition. The callback accepts an
   // optional string specifying an error message, if any.
-  void HandleStop(ApiCallback callback);
+  void HandleStop(OnStopCallback callback);
 
   std::string locale() { return locale_; }
   bool interim_results() { return interim_results_; }
@@ -64,10 +68,9 @@
   void RecognizerOff();
 
   // Updates properties used for speech recognition.
-  void MaybeUpdateProperties(
-      absl::optional<std::string> locale,
-      absl::optional<bool> interim_results,
-      base::OnceCallback<void(absl::optional<std::string>)> callback);
+  void MaybeUpdateProperties(absl::optional<std::string> locale,
+                             absl::optional<bool> interim_results,
+                             OnStartCallback callback);
 
   base::WeakPtr<SpeechRecognitionPrivateRecognizer> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
@@ -76,9 +79,13 @@
   SpeechRecognizerStatus current_state_ = SPEECH_RECOGNIZER_OFF;
   std::string locale_ = speech::kUsEnglishLocale;
   bool interim_results_ = false;
+  // The type of speech recognition being used. Default to kNetwork for
+  // initialization purposes. `type_` will always be assigned before speech
+  // recognition starts.
+  speech::SpeechRecognitionType type_ = speech::SpeechRecognitionType::kNetwork;
   // A callback that is run when speech recognition starts. Note, this is
   // updated whenever HandleStart() is called.
-  ApiCallback on_start_callback_;
+  OnStartCallback on_start_callback_;
   // Delegate that helps handle speech recognition events. `delegate_` is
   // required to outlive this object.
   SpeechRecognitionPrivateDelegate* const delegate_;
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer_browsertest.cc b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer_browsertest.cc
index 9edf65d..579a5fa 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer_browsertest.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/chromeos/extensions/speech/speech_recognition_private_base_test.h"
 #include "chrome/browser/chromeos/extensions/speech/speech_recognition_private_delegate.h"
 #include "chrome/browser/speech/fake_speech_recognition_service.h"
+#include "chrome/browser/speech/speech_recognition_constants.h"
 #include "content/public/test/fake_speech_recognition_manager.h"
 
 namespace {
@@ -85,14 +86,26 @@
         locale, interim_results,
         base::BindOnce(&SpeechRecognitionPrivateRecognizerTest::OnStartCallback,
                        base::Unretained(this)));
+
     WaitForRecognitionStarted();
+
+    // Make assertions.
+    speech::SpeechRecognitionType expected_type = GetParam();
+    ASSERT_EQ(expected_type, type());
+    ASSERT_TRUE(ran_on_start_callback());
+    ASSERT_EQ(SPEECH_RECOGNIZER_RECOGNIZING, recognizer()->current_state());
   }
 
   void HandleStopAndWait() {
     recognizer_->HandleStop(base::BindOnce(
         &SpeechRecognitionPrivateRecognizerTest::OnStopOnceCallback,
         base::Unretained(this)));
+
     WaitForRecognitionStopped();
+
+    // Make assertions.
+    ASSERT_TRUE(ran_on_stop_once_callback());
+    ASSERT_EQ(SPEECH_RECOGNIZER_OFF, recognizer()->current_state());
   }
 
   void MaybeUpdateProperties(absl::optional<std::string> locale,
@@ -111,7 +124,9 @@
     recognizer_->OnSpeechResult(transcript, false, absl::nullopt);
   }
 
-  void OnStartCallback(absl::optional<std::string> error) {
+  void OnStartCallback(speech::SpeechRecognitionType type,
+                       absl::optional<std::string> error) {
+    type_ = type;
     on_start_callback_error_ = error.has_value() ? error.value() : "";
     ran_on_start_callback_ = true;
   }
@@ -127,6 +142,7 @@
 
   SpeechRecognitionPrivateRecognizer* recognizer() { return recognizer_.get(); }
 
+  speech::SpeechRecognitionType type() { return type_; }
   bool ran_on_start_callback() { return ran_on_start_callback_; }
   void set_ran_on_start_callback(bool value) { ran_on_start_callback_ = value; }
 
@@ -150,6 +166,7 @@
   bool last_is_final() { return delegate_->last_is_final_; }
   std::string last_error() { return delegate_->last_error_; }
 
+  speech::SpeechRecognitionType type_ = speech::SpeechRecognitionType::kNetwork;
   bool ran_on_start_callback_ = false;
   bool ran_on_stop_once_callback_ = false;
   std::string on_start_callback_error_;
@@ -159,13 +176,15 @@
   std::unique_ptr<FakeSpeechRecognitionPrivateDelegate> delegate_;
 };
 
-INSTANTIATE_TEST_SUITE_P(Network,
-                         SpeechRecognitionPrivateRecognizerTest,
-                         ::testing::Values(SpeechRecognitionType::kNetwork));
+INSTANTIATE_TEST_SUITE_P(
+    Network,
+    SpeechRecognitionPrivateRecognizerTest,
+    ::testing::Values(speech::SpeechRecognitionType::kNetwork));
 
-INSTANTIATE_TEST_SUITE_P(OnDevice,
-                         SpeechRecognitionPrivateRecognizerTest,
-                         ::testing::Values(SpeechRecognitionType::kOnDevice));
+INSTANTIATE_TEST_SUITE_P(
+    OnDevice,
+    SpeechRecognitionPrivateRecognizerTest,
+    ::testing::Values(speech::SpeechRecognitionType::kOnDevice));
 
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateRecognizerTest,
                        MaybeUpdateProperties) {
@@ -196,8 +215,6 @@
   absl::optional<std::string> locale;
   absl::optional<bool> interim_results;
   HandleStartAndWait(locale, interim_results);
-  ASSERT_EQ(SPEECH_RECOGNIZER_RECOGNIZING, recognizer()->current_state());
-  ASSERT_TRUE(ran_on_start_callback());
 }
 
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateRecognizerTest,
@@ -207,13 +224,9 @@
 
   // Start speech recognition.
   HandleStartAndWait(locale, interim_results);
-  ASSERT_EQ(SPEECH_RECOGNIZER_RECOGNIZING, recognizer()->current_state());
-  ASSERT_TRUE(ran_on_start_callback());
 
   // Stop speech recognition.
   HandleStopAndWait();
-  ASSERT_EQ(SPEECH_RECOGNIZER_OFF, recognizer()->current_state());
-  ASSERT_TRUE(ran_on_stop_once_callback());
   ASSERT_TRUE(delegate_handled_stop());
 }
 
@@ -224,9 +237,7 @@
   absl::optional<std::string> locale;
   absl::optional<bool> interim_results;
   HandleStartAndWait(locale, interim_results);
-  ASSERT_TRUE(ran_on_start_callback());
   ASSERT_EQ("", on_start_callback_error());
-  ASSERT_EQ(SPEECH_RECOGNIZER_RECOGNIZING, recognizer()->current_state());
   ASSERT_EQ(kEnglishLocale, recognizer()->locale());
   ASSERT_FALSE(recognizer()->interim_results());
 
@@ -248,15 +259,11 @@
   absl::optional<std::string> locale;
   absl::optional<bool> interim_results;
   HandleStartAndWait(locale, interim_results);
-  ASSERT_TRUE(ran_on_start_callback());
-  ASSERT_EQ(SPEECH_RECOGNIZER_RECOGNIZING, recognizer()->current_state());
   ASSERT_EQ(kEnglishLocale, recognizer()->locale());
   ASSERT_EQ(false, recognizer()->interim_results());
 
   HandleStopAndWait();
-  ASSERT_TRUE(ran_on_stop_once_callback());
   ASSERT_TRUE(delegate_handled_stop());
-  ASSERT_EQ(SPEECH_RECOGNIZER_OFF, recognizer()->current_state());
   ASSERT_EQ(kEnglishLocale, recognizer()->locale());
   ASSERT_EQ(false, recognizer()->interim_results());
 
@@ -269,15 +276,11 @@
   set_ran_on_stop_once_callback(false);
   set_delegate_handled_stop(false);
   HandleStartAndWait(locale, interim_results);
-  ASSERT_TRUE(ran_on_start_callback());
-  ASSERT_EQ(SPEECH_RECOGNIZER_RECOGNIZING, recognizer()->current_state());
   ASSERT_EQ(kEnglishLocale, recognizer()->locale());
   ASSERT_EQ(true, recognizer()->interim_results());
 
   HandleStopAndWait();
-  ASSERT_TRUE(ran_on_stop_once_callback());
   ASSERT_TRUE(delegate_handled_stop());
-  ASSERT_EQ(SPEECH_RECOGNIZER_OFF, recognizer()->current_state());
   ASSERT_EQ(kEnglishLocale, recognizer()->locale());
   ASSERT_EQ(true, recognizer()->interim_results());
 }
@@ -288,11 +291,9 @@
 IN_PROC_BROWSER_TEST_P(SpeechRecognitionPrivateRecognizerTest,
                        HandleStopNeverStarted) {
   HandleStopAndWait();
-  ASSERT_TRUE(ran_on_stop_once_callback());
   ASSERT_EQ("Speech recognition already stopped",
             on_stop_once_callback_error());
   ASSERT_FALSE(delegate_handled_stop());
-  ASSERT_EQ(SPEECH_RECOGNIZER_OFF, recognizer()->current_state());
   ASSERT_EQ(kEnglishLocale, recognizer()->locale());
   ASSERT_EQ(false, recognizer()->interim_results());
 }
diff --git a/chrome/browser/speech/speech_recognition_constants.cc b/chrome/browser/speech/speech_recognition_constants.cc
new file mode 100644
index 0000000..7ee9cee2
--- /dev/null
+++ b/chrome/browser/speech/speech_recognition_constants.cc
@@ -0,0 +1,24 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/speech/speech_recognition_constants.h"
+#include "base/notreached.h"
+
+namespace speech {
+
+extensions::api::speech_recognition_private::SpeechRecognitionType
+SpeechRecognitionTypeToApiType(SpeechRecognitionType type) {
+  switch (type) {
+    case SpeechRecognitionType::kNetwork:
+      return extensions::api::speech_recognition_private::
+          SpeechRecognitionType::SPEECH_RECOGNITION_TYPE_NETWORK;
+    case SpeechRecognitionType::kOnDevice:
+      return extensions::api::speech_recognition_private::
+          SpeechRecognitionType::SPEECH_RECOGNITION_TYPE_ONDEVICE;
+  }
+
+  NOTREACHED();
+}
+
+}  // namespace speech
diff --git a/chrome/browser/speech/speech_recognition_constants.h b/chrome/browser/speech/speech_recognition_constants.h
new file mode 100644
index 0000000..0e2e96e
--- /dev/null
+++ b/chrome/browser/speech/speech_recognition_constants.h
@@ -0,0 +1,20 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note, this file is only ever used on Chrome OS.
+#ifndef CHROME_BROWSER_SPEECH_SPEECH_RECOGNITION_CONSTANTS_H_
+#define CHROME_BROWSER_SPEECH_SPEECH_RECOGNITION_CONSTANTS_H_
+
+#include "chrome/common/extensions/api/speech_recognition_private.h"
+
+namespace speech {
+
+enum class SpeechRecognitionType { kNetwork, kOnDevice };
+
+extensions::api::speech_recognition_private::SpeechRecognitionType
+SpeechRecognitionTypeToApiType(SpeechRecognitionType type);
+
+}  // namespace speech
+
+#endif  // CHROME_BROWSER_SPEECH_SPEECH_RECOGNITION_CONSTANTS_H_
diff --git a/chrome/browser/speech/speech_recognition_test_helper.cc b/chrome/browser/speech/speech_recognition_test_helper.cc
index 752e48e..28b0525c 100644
--- a/chrome/browser/speech/speech_recognition_test_helper.cc
+++ b/chrome/browser/speech/speech_recognition_test_helper.cc
@@ -9,18 +9,19 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
 #include "chrome/browser/speech/fake_speech_recognition_service.h"
+#include "chrome/browser/speech/speech_recognition_constants.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/soda/soda_installer.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/test/fake_speech_recognition_manager.h"
 
 SpeechRecognitionTestHelper::SpeechRecognitionTestHelper(
-    SpeechRecognitionType type)
+    speech::SpeechRecognitionType type)
     : type_(type) {}
 SpeechRecognitionTestHelper::~SpeechRecognitionTestHelper() = default;
 
 void SpeechRecognitionTestHelper::SetUp(Profile* profile) {
-  if (type_ == SpeechRecognitionType::kNetwork)
+  if (type_ == speech::SpeechRecognitionType::kNetwork)
     SetUpNetworkRecognition();
   else
     SetUpOnDeviceRecognition(profile);
@@ -57,10 +58,10 @@
 
 void SpeechRecognitionTestHelper::WaitForRecognitionStarted() {
   // Only wait for recognition to start if it hasn't been started yet.
-  if (type_ == SpeechRecognitionType::kNetwork &&
+  if (type_ == speech::SpeechRecognitionType::kNetwork &&
       !fake_speech_recognition_manager_->is_recognizing()) {
     fake_speech_recognition_manager_->WaitForRecognitionStarted();
-  } else if (type_ == SpeechRecognitionType::kOnDevice &&
+  } else if (type_ == speech::SpeechRecognitionType::kOnDevice &&
              !fake_service_->is_capturing_audio()) {
     fake_service_->WaitForRecognitionStarted();
   }
@@ -69,7 +70,7 @@
 
 void SpeechRecognitionTestHelper::WaitForRecognitionStopped() {
   // Only wait for recognition to stop if it hasn't been stopped yet.
-  if (type_ == SpeechRecognitionType::kNetwork &&
+  if (type_ == speech::SpeechRecognitionType::kNetwork &&
       fake_speech_recognition_manager_->is_recognizing()) {
     fake_speech_recognition_manager_->WaitForRecognitionEnded();
   }
@@ -80,7 +81,7 @@
     const std::string& transcript,
     bool is_final) {
   base::RunLoop loop;
-  if (type_ == SpeechRecognitionType::kNetwork) {
+  if (type_ == speech::SpeechRecognitionType::kNetwork) {
     // FakeSpeechRecognitionManager can only send final results, so this method
     // shouldn't be called if `is_final` is false.
     DCHECK(is_final);
@@ -103,7 +104,7 @@
 
 void SpeechRecognitionTestHelper::SendFakeSpeechRecognitionErrorAndWait() {
   base::RunLoop loop;
-  if (type_ == SpeechRecognitionType::kNetwork) {
+  if (type_ == speech::SpeechRecognitionType::kNetwork) {
     fake_speech_recognition_manager_->SendFakeError(loop.QuitClosure());
     loop.Run();
   } else {
@@ -114,14 +115,14 @@
 
 std::vector<base::Feature> SpeechRecognitionTestHelper::GetEnabledFeatures() {
   std::vector<base::Feature> features;
-  if (type_ == SpeechRecognitionType::kOnDevice)
+  if (type_ == speech::SpeechRecognitionType::kOnDevice)
     features.push_back(ash::features::kOnDeviceSpeechRecognition);
   return features;
 }
 
 std::vector<base::Feature> SpeechRecognitionTestHelper::GetDisabledFeatures() {
   std::vector<base::Feature> features;
-  if (type_ == SpeechRecognitionType::kNetwork)
+  if (type_ == speech::SpeechRecognitionType::kNetwork)
     features.push_back(ash::features::kOnDeviceSpeechRecognition);
   return features;
 }
diff --git a/chrome/browser/speech/speech_recognition_test_helper.h b/chrome/browser/speech/speech_recognition_test_helper.h
index c88217d..20835a0 100644
--- a/chrome/browser/speech/speech_recognition_test_helper.h
+++ b/chrome/browser/speech/speech_recognition_test_helper.h
@@ -23,10 +23,9 @@
 
 namespace speech {
 class FakeSpeechRecognitionService;
+enum class SpeechRecognitionType;
 }  // namespace speech
 
-enum class SpeechRecognitionType { kNetwork, kOnDevice };
-
 // This class provides on-device and network speech recognition test
 // infrastructure. Test classes can use this one to easily interact with
 // speech recognizers. For example:
@@ -40,7 +39,7 @@
 // DictationBaseTest.
 class SpeechRecognitionTestHelper {
  public:
-  explicit SpeechRecognitionTestHelper(SpeechRecognitionType type);
+  explicit SpeechRecognitionTestHelper(speech::SpeechRecognitionType type);
   ~SpeechRecognitionTestHelper();
   SpeechRecognitionTestHelper(const SpeechRecognitionTestHelper&) = delete;
   SpeechRecognitionTestHelper& operator=(const SpeechRecognitionTestHelper&) =
@@ -71,7 +70,7 @@
   std::unique_ptr<KeyedService> CreateTestOnDeviceSpeechRecognitionService(
       content::BrowserContext* context);
 
-  SpeechRecognitionType type_;
+  speech::SpeechRecognitionType type_;
   // For network recognition.
   std::unique_ptr<content::FakeSpeechRecognitionManager>
       fake_speech_recognition_manager_;
diff --git a/chrome/common/extensions/api/speech_recognition_private.idl b/chrome/common/extensions/api/speech_recognition_private.idl
index 58fabe24..2ea3223 100644
--- a/chrome/common/extensions/api/speech_recognition_private.idl
+++ b/chrome/common/extensions/api/speech_recognition_private.idl
@@ -7,6 +7,12 @@
 [platforms=("chromeos"), implemented_in="chrome/browser/chromeos/extensions/speech/speech_recognition_private_api.h"]
 
 namespace speechRecognitionPrivate {
+  // Possible types of speech recognition.
+  enum SpeechRecognitionType {
+    onDevice,
+    network
+  };
+
   // Interface for an onStop event.
   dictionary SpeechRecognitionStopEvent {
     // Optional client ID.
@@ -49,8 +55,10 @@
   };
 
   // Called when speech recognition has begun listening to the user's audio.
-  callback OnStartCallback = void();
-  // Called when speech recogntion has stopped listening to the user's audio.
+  // The callback's parameter specifies which type of speech recognition
+  // is being used.
+  callback OnStartCallback = void(SpeechRecognitionType type);
+  // Called when speech recognition has stopped listening to the user's audio.
   callback OnStopCallback = void();
 
   interface Functions {
diff --git a/chrome/test/data/extensions/api_test/speech/speech_recognition_private/on_start/manifest.json b/chrome/test/data/extensions/api_test/speech/speech_recognition_private/on_start/manifest.json
new file mode 100644
index 0000000..cc9b331
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/speech/speech_recognition_private/on_start/manifest.json
@@ -0,0 +1,15 @@
+{
+  "name": "OnStart",
+  // Use the manifest key for AccessibilityCommon, which is one of the extensions
+  // in the speechRecognitionPrivate allowlist.
+  "key": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6SX/t6kDEi9UiG4fULwjbEW1uJmQoiJPtxvSZ/SDH14OjSzmfP6xfE97Hus3EY8uSIMxTHkGaZliGbFbIQXQn6/XwBpQRu2gPlrfz7TWR1Hw6SboBBMOpltM9A2nx+d3jLtz+YtKNYVcHyNil9hXfFeyFe6g5kLHapKb1UO0jo3q3kovo1a1z7ujzGwogfLmb58w1hkdFBnnqumRlT55dKLN8AQ6cSdB1sjDVoMgPYeWgkzXr9cR3A8UVJookSO0sDAmD+W8BtBijapt3UVkHiIL1NTPuXvGGUHL7TPFo5WcBXFMkTz74gJqqFdO5hQ2YWXAaCxQJwgJrQPrvPMSJAgMBAAECggEADDhEDww9wWbWzUz3BQEs2In1HrOgAFStN3zEkNFc9B78AJsvpXWczgPUqk9jrg1JzkUeghlK/mDWT8MNkkdQ4kmFMYCM9/jOI6+kU3js+arxlzU84VI5r4c4RhlSOtBEMOHjF0DORP3sopMXOxPAbYjXog3xhA0szYXdedwcIik7Xu3lt1Hl5FfVZbvVLdf4vw0jTfHcp8SmHy/BDVnSCrhC3pnPGi6o+lUaSK0ca3uvcJDZGLXJ/6LyFb6uLlS2XUoBMYsombioRKrerJJSOmMTLHvfu1cM6+iQ+J0wdBnJQpgmDoSVGjnksPU2SMpWgG2OzwuZYIUGI745s19wLQKBgQDvdHsMZ4ttBr9bjydzeZVATWTICHZgXdAYgfgrbGwppYDUjfKoAuJ6bHTvff4nj8aZrY+Y1SwuvqxgHHfiggUgqg+JyeaAdQG+CLdfl1M8An+6H0x/hx0nk0oOJQhu0y1R/SbtnDJ6JASszg/VrTwHIYbzUl6xKHbZ6X41apyLYwKBgQDHKJOeZdxuYj7AsAqFGreuPoAEh0S+2VHHi4rjNz5dC1z7o/8siixfkHg7ONM2hqCKo55XYj4UWtprEFZJ9ohbizHELNzpnTxjdS0cG/VfItml6CDJaUtrkShIx17yGjNi0u/7ywHQ3slJsUXu7CbEcESwEzdoSrsC048dyxBSIwKBgF0141wtxklXcg/LBtldf6q7NbrkCGh0vDd+CEOm/eesRBz5cHbUQKLVKyO60L9HqVBTDm24tW0wzdrP2h7y69oOOOQzEqX4Zgg6Tl9IgZ7/fgbOfjG6P7ATFqWw5rp1O9QJjii6P6/p62P1Bpbvy0kfVO/MpY2iqbkjufxDFtLvAoGBAMC5p4CVGedH82oL8WI1JKLdoIzBSelV7CmqA9E1WIg5wtVRMlIrtB0WdQL6ToppZVpEU6pES8bu1Ibe3GHezL2pyZMJxw3bNuEYN3sIIz7ZPr2qEHBYEMAbTFyBcoPejvOHJO0I2s0BitBhWEeJB0r5Sb8KGYg3KRnnGIvAQh75AoGBANEC/k1umGrnMO3rwHJF7R+aTHzeMnO6oi11pmSnT7eJcF+oi7OwHS3ickU6sGrIb5QmnwCY9ES1qY6mP7N++KQGsdQM2l13MpCn8cBZgrfpQg2slP1dz8LCDW/PB+6MF7qwEHN2afVA2muQaez+q0eXZjMXmGJ3VZIXz/cxBLD6",
+  "version": "0.1",
+  "manifest_version": 3,
+  "description": "Browser test for chrome.speechRecognitionPrivate.start() callback",
+  "background": {
+    "service_worker": "test.js"
+  },
+  "permissions": [
+    "speechRecognitionPrivate"
+  ]
+}
diff --git a/chrome/test/data/extensions/api_test/speech/speech_recognition_private/on_start/test.js b/chrome/test/data/extensions/api_test/speech/speech_recognition_private/on_start/test.js
new file mode 100644
index 0000000..aa5e654
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/speech/speech_recognition_private/on_start/test.js
@@ -0,0 +1,16 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  const onDevice =
+      chrome.speechRecognitionPrivate.SpeechRecognitionType.ON_DEVICE;
+  const network = chrome.speechRecognitionPrivate.SpeechRecognitionType.NETWORK;
+  const config = await chrome.test.getConfig();
+  const expectedRecognitionType = config.customArg;
+  const actualRecognitionType = await chrome.speechRecognitionPrivate.start({});
+  chrome.test.assertTrue(
+      actualRecognitionType === onDevice || actualRecognitionType === network);
+  chrome.test.assertEq(expectedRecognitionType, actualRecognitionType);
+  chrome.test.succeed();
+})();
diff --git a/chrome/test/data/extensions/api_test/speech/speech_recognition_private/start_error_stop/test.js b/chrome/test/data/extensions/api_test/speech/speech_recognition_private/start_error_stop/test.js
index 48a6094e..7ddc0b0 100644
--- a/chrome/test/data/extensions/api_test/speech/speech_recognition_private/start_error_stop/test.js
+++ b/chrome/test/data/extensions/api_test/speech/speech_recognition_private/start_error_stop/test.js
@@ -7,7 +7,7 @@
     chrome.speechRecognitionPrivate.onError.addListener((event) => {
       chrome.test.assertEq(
           'A speech recognition error occurred', event.message);
-      resolve()
+      resolve();
     });
   });
 
diff --git a/third_party/closure_compiler/externs/speech_recognition_private.js b/third_party/closure_compiler/externs/speech_recognition_private.js
index 871ba18a..da4da0c7 100644
--- a/third_party/closure_compiler/externs/speech_recognition_private.js
+++ b/third_party/closure_compiler/externs/speech_recognition_private.js
@@ -15,6 +15,14 @@
 chrome.speechRecognitionPrivate = {};
 
 /**
+ * @enum {string}
+ */
+chrome.speechRecognitionPrivate.SpeechRecognitionType = {
+  ON_DEVICE: 'onDevice',
+  NETWORK: 'network',
+};
+
+/**
  * @typedef {{
  *   clientId: (number|undefined)
  * }}
@@ -59,8 +67,10 @@
  * recognition has started. If speech recognition is already active when calling
  * start(), the callback is run with an error.
  * @param {!chrome.speechRecognitionPrivate.StartOptions} options
- * @param {function(): void} callback Called when speech recognition has begun
- *     listening to the user's audio.
+ * @param {function(!chrome.speechRecognitionPrivate.SpeechRecognitionType): void}
+ *     callback Called when speech recognition has begun listening to the user's
+ *     audio. The callback's parameter specifies which type of speech
+ *     recognition is being used.
  */
 chrome.speechRecognitionPrivate.start = function(options, callback) {};
 
@@ -69,7 +79,7 @@
  * recognition has stopped. If speech recognition has already stopped when
  * calling stop(), the callback is run with an error.
  * @param {!chrome.speechRecognitionPrivate.StopOptions} options
- * @param {function(): void} callback Called when speech recogntion has stopped
+ * @param {function(): void} callback Called when speech recognition has stopped
  *     listening to the user's audio.
  */
 chrome.speechRecognitionPrivate.stop = function(options, callback) {};