Add an option to avoid early initialization of audio capture

This can cause issues on Android S if this initialization happens when
the app doesn't have permission to access the microphone.

Bug: b/197461765
Change-Id: Iebccff9d15f5bb12a7b2c78e1c373e379b37a127
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/246104
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Commit-Queue: Xavier Lepaul‎ <xalep@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35689}
diff --git a/api/audio_options.cc b/api/audio_options.cc
index 6832bbe..aca1fe8 100644
--- a/api/audio_options.cc
+++ b/api/audio_options.cc
@@ -65,6 +65,7 @@
   SetFrom(&combined_audio_video_bwe, change.combined_audio_video_bwe);
   SetFrom(&audio_network_adaptor, change.audio_network_adaptor);
   SetFrom(&audio_network_adaptor_config, change.audio_network_adaptor_config);
+  SetFrom(&init_recording_on_send, change.init_recording_on_send);
 }
 
 bool AudioOptions::operator==(const AudioOptions& o) const {
@@ -92,7 +93,8 @@
          tx_agc_limiter == o.tx_agc_limiter &&
          combined_audio_video_bwe == o.combined_audio_video_bwe &&
          audio_network_adaptor == o.audio_network_adaptor &&
-         audio_network_adaptor_config == o.audio_network_adaptor_config;
+         audio_network_adaptor_config == o.audio_network_adaptor_config &&
+         init_recording_on_send == o.init_recording_on_send;
 }
 
 std::string AudioOptions::ToString() const {
@@ -126,6 +128,7 @@
   ToStringIfSet(&result, "tx_agc_limiter", tx_agc_limiter);
   ToStringIfSet(&result, "combined_audio_video_bwe", combined_audio_video_bwe);
   ToStringIfSet(&result, "audio_network_adaptor", audio_network_adaptor);
+  ToStringIfSet(&result, "init_recording_on_send", init_recording_on_send);
   result << "}";
   return result.str();
 }
diff --git a/api/audio_options.h b/api/audio_options.h
index 7d933ae..48dd628 100644
--- a/api/audio_options.h
+++ b/api/audio_options.h
@@ -83,6 +83,10 @@
   absl::optional<bool> audio_network_adaptor;
   // Config string for audio network adaptor.
   absl::optional<std::string> audio_network_adaptor_config;
+  // Pre-initialize the ADM for recording when starting to send. Default to
+  // true.
+  // TODO(webrtc:13566): Remove this option. See issue for details.
+  absl::optional<bool> init_recording_on_send;
 };
 
 }  // namespace cricket
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 0a8d3da..536df8e 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -1862,13 +1862,15 @@
     return;
   }
 
-  // Apply channel specific options, and initialize the ADM for recording (this
-  // may take time on some platforms, e.g. Android).
+  // Apply channel specific options.
   if (send) {
     engine()->ApplyOptions(options_);
 
-    // InitRecording() may return an error if the ADM is already recording.
-    if (!engine()->adm()->RecordingIsInitialized() &&
+    // Initialize the ADM for recording (this may take time on some platforms,
+    // e.g. Android).
+    if (options_.init_recording_on_send.value_or(true) &&
+        // InitRecording() may return an error if the ADM is already recording.
+        !engine()->adm()->RecordingIsInitialized() &&
         !engine()->adm()->Recording()) {
       if (engine()->adm()->InitRecording() != 0) {
         RTC_LOG(LS_WARNING) << "Failed to initialize recording";
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index 97e46b2..2770a0d 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -305,9 +305,15 @@
   void SetSend(bool enable) {
     ASSERT_TRUE(channel_);
     if (enable) {
-      EXPECT_CALL(*adm_, RecordingIsInitialized()).WillOnce(Return(false));
-      EXPECT_CALL(*adm_, Recording()).WillOnce(Return(false));
-      EXPECT_CALL(*adm_, InitRecording()).WillOnce(Return(0));
+      EXPECT_CALL(*adm_, RecordingIsInitialized())
+          .Times(::testing::AtMost(1))
+          .WillOnce(Return(false));
+      EXPECT_CALL(*adm_, Recording())
+          .Times(::testing::AtMost(1))
+          .WillOnce(Return(false));
+      EXPECT_CALL(*adm_, InitRecording())
+          .Times(::testing::AtMost(1))
+          .WillOnce(Return(0));
     }
     channel_->SetSend(enable);
   }
@@ -3122,6 +3128,34 @@
   }
 }
 
+TEST_P(WebRtcVoiceEngineTestFake, InitRecordingOnSend) {
+  EXPECT_CALL(*adm_, RecordingIsInitialized()).WillOnce(Return(false));
+  EXPECT_CALL(*adm_, Recording()).WillOnce(Return(false));
+  EXPECT_CALL(*adm_, InitRecording()).Times(1);
+
+  std::unique_ptr<cricket::VoiceMediaChannel> channel(
+      engine_->CreateMediaChannel(&call_, cricket::MediaConfig(),
+                                  cricket::AudioOptions(),
+                                  webrtc::CryptoOptions()));
+
+  channel->SetSend(true);
+}
+
+TEST_P(WebRtcVoiceEngineTestFake, SkipInitRecordingOnSend) {
+  EXPECT_CALL(*adm_, RecordingIsInitialized()).Times(0);
+  EXPECT_CALL(*adm_, Recording()).Times(0);
+  EXPECT_CALL(*adm_, InitRecording()).Times(0);
+
+  cricket::AudioOptions options;
+  options.init_recording_on_send = false;
+
+  std::unique_ptr<cricket::VoiceMediaChannel> channel(
+      engine_->CreateMediaChannel(&call_, cricket::MediaConfig(), options,
+                                  webrtc::CryptoOptions()));
+
+  channel->SetSend(true);
+}
+
 TEST_P(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) {
   EXPECT_TRUE(SetupSendStream());
   EXPECT_CALL(*adm_, BuiltInAECIsAvailable())
diff --git a/sdk/media_constraints.cc b/sdk/media_constraints.cc
index 08bd9dc..3cc08b2 100644
--- a/sdk/media_constraints.cc
+++ b/sdk/media_constraints.cc
@@ -106,6 +106,8 @@
 const char MediaConstraints::kAudioMirroring[] = "googAudioMirroring";
 const char MediaConstraints::kAudioNetworkAdaptorConfig[] =
     "googAudioNetworkAdaptorConfig";
+const char MediaConstraints::kInitAudioRecordingOnSend[] =
+    "InitAudioRecordingOnSend";
 
 // Constraint keys for CreateOffer / CreateAnswer defined in W3C specification.
 const char MediaConstraints::kOfferToReceiveAudio[] = "OfferToReceiveAudio";
@@ -211,6 +213,9 @@
   if (options->audio_network_adaptor_config) {
     options->audio_network_adaptor = true;
   }
+  ConstraintToOptional<bool>(constraints,
+                             MediaConstraints::kInitAudioRecordingOnSend,
+                             &options->init_recording_on_send);
 }
 
 bool CopyConstraintsIntoOfferAnswerOptions(
diff --git a/sdk/media_constraints.h b/sdk/media_constraints.h
index fd95a60..f41ad25 100644
--- a/sdk/media_constraints.h
+++ b/sdk/media_constraints.h
@@ -67,7 +67,8 @@
   static const char kTypingNoiseDetection[];  // googTypingNoiseDetection
   static const char kAudioMirroring[];        // googAudioMirroring
   static const char
-      kAudioNetworkAdaptorConfig[];  // goodAudioNetworkAdaptorConfig
+      kAudioNetworkAdaptorConfig[];  // googAudioNetworkAdaptorConfig
+  static const char kInitAudioRecordingOnSend[];  // InitAudioRecordingOnSend;
 
   // Constraint keys for CreateOffer / CreateAnswer
   // Specified by the W3C PeerConnection spec