Adds new CryptoOption crypto_options.frame.require_frame_encryption.

This change adds a new subcategory to the public native webrtc::CryptoOptions
structure: webrtc::CryptoOptions::Frame.

This new structure has a single off by default property:
crypto_options.frame.require_frame_encryption.

This new flag if set prevents RtpSenders from sending outgoing payloads unless
a frame_encryptor_ is attached and prevents RtpReceivers from receiving
incoming payloads unless a frame_decryptor_ is attached.

This option is important to enforce no unencrypted data can ever leave the
device or be received.

I have also attached bindings for Java and Objective-C.

I have implemented this functionality for E2EE audio but not E2EE video
since the changes are still in review.

Bug: webrtc:9681
Change-Id: Ie184711190e0cdf5ac781f69e9489ceec904736f
Reviewed-on: https://webrtc-review.googlesource.com/c/105540
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Commit-Queue: Benjamin Wright <benwright@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25238}
diff --git a/api/crypto/cryptooptions.cc b/api/crypto/cryptooptions.cc
index 2c34822..7f34f19 100644
--- a/api/crypto/cryptooptions.cc
+++ b/api/crypto/cryptooptions.cc
@@ -17,6 +17,7 @@
 
 CryptoOptions::CryptoOptions(const CryptoOptions& other) {
   srtp = other.srtp;
+  sframe = other.sframe;
 }
 
 CryptoOptions::~CryptoOptions() {}
@@ -46,4 +47,32 @@
   return crypto_suites;
 }
 
+bool CryptoOptions::operator==(const CryptoOptions& other) const {
+  struct data_being_tested_for_equality {
+    struct Srtp {
+      bool enable_gcm_crypto_suites;
+      bool enable_aes128_sha1_32_crypto_cipher;
+      bool enable_encrypted_rtp_header_extensions;
+    } srtp;
+    struct SFrame {
+      bool require_frame_encryption;
+    } sframe;
+  };
+  static_assert(sizeof(data_being_tested_for_equality) == sizeof(*this),
+                "Did you add something to CryptoOptions and forget to "
+                "update operator==?");
+
+  return srtp.enable_gcm_crypto_suites == other.srtp.enable_gcm_crypto_suites &&
+         srtp.enable_aes128_sha1_32_crypto_cipher ==
+             other.srtp.enable_aes128_sha1_32_crypto_cipher &&
+         srtp.enable_encrypted_rtp_header_extensions ==
+             other.srtp.enable_encrypted_rtp_header_extensions &&
+         sframe.require_frame_encryption ==
+             other.sframe.require_frame_encryption;
+}
+
+bool CryptoOptions::operator!=(const CryptoOptions& other) const {
+  return !(*this == other);
+}
+
 }  // namespace webrtc
diff --git a/api/crypto/cryptooptions.h b/api/crypto/cryptooptions.h
index de674c2..bd4a1c4 100644
--- a/api/crypto/cryptooptions.h
+++ b/api/crypto/cryptooptions.h
@@ -33,6 +33,9 @@
   // of crypto options.
   std::vector<int> GetSupportedDtlsSrtpCryptoSuites() const;
 
+  bool operator==(const CryptoOptions& other) const;
+  bool operator!=(const CryptoOptions& other) const;
+
   // SRTP Related Peer Connection options.
   struct Srtp {
     // Enable GCM crypto suites from RFC 7714 for SRTP. GCM will only be used
@@ -49,6 +52,14 @@
     // will be negotiated. They will only be used if both peers support them.
     bool enable_encrypted_rtp_header_extensions = false;
   } srtp;
+
+  // Options to be used when the FrameEncryptor / FrameDecryptor APIs are used.
+  struct SFrame {
+    // If set all RtpSenders must have an FrameEncryptor attached to them before
+    // they are allowed to send packets. All RtpReceivers must have a
+    // FrameDecryptor attached to them before they are able to receive packets.
+    bool require_frame_encryption = false;
+  } sframe;
 };
 
 }  // namespace webrtc
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index 563e8a0..bf06cd2 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -76,7 +76,7 @@
           config.rtcp_send_transport, event_log, config.rtp.remote_ssrc,
           config.jitter_buffer_max_packets,
           config.jitter_buffer_fast_accelerate, config.decoder_factory,
-          config.codec_pair_id, config.frame_decryptor));
+          config.codec_pair_id, config.frame_decryptor, config.crypto_options));
 }
 }  // namespace
 
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 49d933c..39cb4e8 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -51,11 +51,12 @@
     ProcessThread* module_process_thread,
     RtcpRttStats* rtcp_rtt_stats,
     RtcEventLog* event_log,
-    FrameEncryptorInterface* frame_encryptor) {
+    FrameEncryptorInterface* frame_encryptor,
+    const webrtc::CryptoOptions& crypto_options) {
   return absl::make_unique<voe::ChannelSendProxy>(
       absl::make_unique<voe::ChannelSend>(worker_queue, module_process_thread,
                                           rtcp_rtt_stats, event_log,
-                                          frame_encryptor));
+                                          frame_encryptor, crypto_options));
 }
 }  // namespace
 
@@ -106,7 +107,8 @@
                                             module_process_thread,
                                             rtcp_rtt_stats,
                                             event_log,
-                                            config.frame_encryptor)) {}
+                                            config.frame_encryptor,
+                                            config.crypto_options)) {}
 
 AudioSendStream::AudioSendStream(
     const webrtc::AudioSendStream::Config& config,
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
index e9f7503..2384999 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
@@ -206,7 +206,8 @@
     bool jitter_buffer_fast_playout,
     rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
     absl::optional<AudioCodecPairId> codec_pair_id,
-    FrameDecryptorInterface* frame_decryptor)
+    FrameDecryptorInterface* frame_decryptor,
+    const webrtc::CryptoOptions& crypto_options)
     : event_log_(rtc_event_log),
       rtp_receive_statistics_(
           ReceiveStatistics::Create(Clock::GetRealTimeClock())),
@@ -222,7 +223,8 @@
       _audioDeviceModulePtr(audio_device_module),
       _outputGain(1.0f),
       associated_send_channel_(nullptr),
-      frame_decryptor_(frame_decryptor) {
+      frame_decryptor_(frame_decryptor),
+      crypto_options_(crypto_options) {
   RTC_DCHECK(module_process_thread);
   RTC_DCHECK(audio_device_module);
   AudioCodingModule::Config acm_config;
@@ -429,6 +431,10 @@
     // Update the final payload.
     payload = decrypted_audio_payload.data();
     payload_data_length = decrypted_audio_payload.size();
+  } else if (crypto_options_.sframe.require_frame_encryption) {
+    RTC_DLOG(LS_ERROR)
+        << "FrameDecryptor required but not set, dropping packet";
+    payload_data_length = 0;
   }
 
   if (payload_data_length == 0) {
diff --git a/audio/channel_receive.h b/audio/channel_receive.h
index 82eb4df..ac359bb 100644
--- a/audio/channel_receive.h
+++ b/audio/channel_receive.h
@@ -19,6 +19,7 @@
 #include "api/audio/audio_mixer.h"
 #include "api/call/audio_sink.h"
 #include "api/call/transport.h"
+#include "api/crypto/cryptooptions.h"
 #include "api/rtpreceiverinterface.h"
 #include "audio/audio_level.h"
 #include "call/syncable.h"
@@ -114,7 +115,8 @@
                  bool jitter_buffer_fast_playout,
                  rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
                  absl::optional<AudioCodecPairId> codec_pair_id,
-                 FrameDecryptorInterface* frame_decryptor);
+                 FrameDecryptorInterface* frame_decryptor,
+                 const webrtc::CryptoOptions& crypto_options);
   virtual ~ChannelReceive();
 
   void SetSink(AudioSinkInterface* sink);
@@ -259,6 +261,7 @@
 
   // E2EE Audio Frame Decryption
   FrameDecryptorInterface* frame_decryptor_ = nullptr;
+  webrtc::CryptoOptions crypto_options_;
 };
 
 }  // namespace voe
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
index 8639fbd..bdabe8f 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -289,6 +289,10 @@
     // Rewrite the payloadData and size to the new encrypted payload.
     payloadData = encrypted_audio_payload.data();
     payloadSize = encrypted_audio_payload.size();
+  } else if (crypto_options_.sframe.require_frame_encryption) {
+    RTC_DLOG(LS_ERROR) << "Channel::SendData() failed sending audio payload: "
+                       << "A frame encryptor is required but one is not set.";
+    return -1;
   }
 
   // Push data from ACM to RTP/RTCP-module to deliver audio frame for
@@ -354,7 +358,8 @@
                          ProcessThread* module_process_thread,
                          RtcpRttStats* rtcp_rtt_stats,
                          RtcEventLog* rtc_event_log,
-                         FrameEncryptorInterface* frame_encryptor)
+                         FrameEncryptorInterface* frame_encryptor,
+                         const webrtc::CryptoOptions& crypto_options)
     : event_log_(rtc_event_log),
       _timeStamp(0),  // This is just an offset, RTP module will add it's own
                       // random offset
@@ -375,7 +380,8 @@
       use_twcc_plr_for_ana_(
           webrtc::field_trial::FindFullName("UseTwccPlrForAna") == "Enabled"),
       encoder_queue_(encoder_queue),
-      frame_encryptor_(frame_encryptor) {
+      frame_encryptor_(frame_encryptor),
+      crypto_options_(crypto_options) {
   RTC_DCHECK(module_process_thread);
   RTC_DCHECK(encoder_queue);
   audio_coding_.reset(AudioCodingModule::Create(AudioCodingModule::Config()));
diff --git a/audio/channel_send.h b/audio/channel_send.h
index ef92f8e..306d6a8 100644
--- a/audio/channel_send.h
+++ b/audio/channel_send.h
@@ -19,6 +19,7 @@
 #include "api/audio/audio_frame.h"
 #include "api/audio_codecs/audio_encoder.h"
 #include "api/call/transport.h"
+#include "api/crypto/cryptooptions.h"
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/include/audio_coding_module.h"
 #include "modules/audio_processing/rms_level.h"
@@ -120,7 +121,8 @@
               ProcessThread* module_process_thread,
               RtcpRttStats* rtcp_rtt_stats,
               RtcEventLog* rtc_event_log,
-              FrameEncryptorInterface* frame_encryptor);
+              FrameEncryptorInterface* frame_encryptor,
+              const webrtc::CryptoOptions& crypto_options);
 
   virtual ~ChannelSend();
 
@@ -298,6 +300,8 @@
 
   // E2EE Audio Frame Encryption
   FrameEncryptorInterface* frame_encryptor_ = nullptr;
+  // E2EE Frame Encryption Options
+  webrtc::CryptoOptions crypto_options_;
 };
 
 }  // namespace voe
diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h
index 9c890b1..786c3ba 100644
--- a/call/audio_receive_stream.h
+++ b/call/audio_receive_stream.h
@@ -19,6 +19,7 @@
 #include "absl/types/optional.h"
 #include "api/audio_codecs/audio_decoder_factory.h"
 #include "api/call/transport.h"
+#include "api/crypto/cryptooptions.h"
 #include "api/rtpparameters.h"
 #include "api/rtpreceiverinterface.h"
 #include "call/rtp_config.h"
@@ -122,6 +123,9 @@
 
     absl::optional<AudioCodecPairId> codec_pair_id;
 
+    // Per PeerConnection crypto options.
+    webrtc::CryptoOptions crypto_options;
+
     // An optional custom frame decryptor that allows the entire frame to be
     // decrypted in whatever way the caller choses. This is not required by
     // default.
diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h
index 61e2531..a659dd6 100644
--- a/call/audio_send_stream.h
+++ b/call/audio_send_stream.h
@@ -21,6 +21,7 @@
 #include "api/audio_codecs/audio_encoder_factory.h"
 #include "api/audio_codecs/audio_format.h"
 #include "api/call/transport.h"
+#include "api/crypto/cryptooptions.h"
 #include "api/crypto/frameencryptorinterface.h"
 #include "api/rtpparameters.h"
 #include "call/rtp_config.h"
@@ -130,6 +131,9 @@
     // Track ID as specified during track creation.
     std::string track_id;
 
+    // Per PeerConnection crypto options.
+    webrtc::CryptoOptions crypto_options;
+
     // An optional custom frame encryptor that allows the entire frame to be
     // encryptor in whatever way the caller choses. This is not required by
     // default.
diff --git a/media/base/fakemediaengine.h b/media/base/fakemediaengine.h
index 62bd6ba..a20beb0 100644
--- a/media/base/fakemediaengine.h
+++ b/media/base/fakemediaengine.h
@@ -776,9 +776,11 @@
     return rtc::scoped_refptr<webrtc::AudioState>();
   }
 
-  VoiceMediaChannel* CreateChannel(webrtc::Call* call,
-                                   const MediaConfig& config,
-                                   const AudioOptions& options) {
+  VoiceMediaChannel* CreateChannel(
+      webrtc::Call* call,
+      const MediaConfig& config,
+      const AudioOptions& options,
+      const webrtc::CryptoOptions& crypto_options) {
     if (fail_create_channel_) {
       return nullptr;
     }
@@ -837,7 +839,8 @@
 
   VideoMediaChannel* CreateChannel(webrtc::Call* call,
                                    const MediaConfig& config,
-                                   const VideoOptions& options) {
+                                   const VideoOptions& options,
+                                   const webrtc::CryptoOptions crypto_options) {
     if (fail_create_channel_) {
       return nullptr;
     }
diff --git a/media/base/mediaengine.h b/media/base/mediaengine.h
index 01c6fb6..e752da8 100644
--- a/media/base/mediaengine.h
+++ b/media/base/mediaengine.h
@@ -22,6 +22,7 @@
 
 #include "api/audio_codecs/audio_decoder_factory.h"
 #include "api/audio_codecs/audio_encoder_factory.h"
+#include "api/crypto/cryptooptions.h"
 #include "api/rtpparameters.h"
 #include "call/audio_state.h"
 #include "media/base/codec.h"
@@ -64,15 +65,18 @@
 
   // MediaChannel creation
   // Creates a voice media channel. Returns NULL on failure.
-  virtual VoiceMediaChannel* CreateChannel(webrtc::Call* call,
-                                           const MediaConfig& config,
-                                           const AudioOptions& options) = 0;
+  virtual VoiceMediaChannel* CreateChannel(
+      webrtc::Call* call,
+      const MediaConfig& config,
+      const AudioOptions& options,
+      const webrtc::CryptoOptions& crypto_options) = 0;
   // Creates a video media channel, paired with the specified voice channel.
   // Returns NULL on failure.
   virtual VideoMediaChannel* CreateVideoChannel(
       webrtc::Call* call,
       const MediaConfig& config,
-      const VideoOptions& options) = 0;
+      const VideoOptions& options,
+      const webrtc::CryptoOptions& crypto_options) = 0;
 
   virtual const std::vector<AudioCodec>& audio_send_codecs() = 0;
   virtual const std::vector<AudioCodec>& audio_recv_codecs() = 0;
@@ -110,15 +114,19 @@
   virtual rtc::scoped_refptr<webrtc::AudioState> GetAudioState() const {
     return voice().GetAudioState();
   }
-  virtual VoiceMediaChannel* CreateChannel(webrtc::Call* call,
-                                           const MediaConfig& config,
-                                           const AudioOptions& options) {
-    return voice().CreateChannel(call, config, options);
+  virtual VoiceMediaChannel* CreateChannel(
+      webrtc::Call* call,
+      const MediaConfig& config,
+      const AudioOptions& options,
+      const webrtc::CryptoOptions& crypto_options) {
+    return voice().CreateChannel(call, config, options, crypto_options);
   }
-  virtual VideoMediaChannel* CreateVideoChannel(webrtc::Call* call,
-                                                const MediaConfig& config,
-                                                const VideoOptions& options) {
-    return video().CreateChannel(call, config, options);
+  virtual VideoMediaChannel* CreateVideoChannel(
+      webrtc::Call* call,
+      const MediaConfig& config,
+      const VideoOptions& options,
+      const webrtc::CryptoOptions& crypto_options) {
+    return video().CreateChannel(call, config, options, crypto_options);
   }
 
   virtual const std::vector<AudioCodec>& audio_send_codecs() {
diff --git a/media/engine/nullwebrtcvideoengine.h b/media/engine/nullwebrtcvideoengine.h
index 9af0f9b..ae519b6 100644
--- a/media/engine/nullwebrtcvideoengine.h
+++ b/media/engine/nullwebrtcvideoengine.h
@@ -36,9 +36,11 @@
 
   RtpCapabilities GetCapabilities() const { return RtpCapabilities(); }
 
-  VideoMediaChannel* CreateChannel(webrtc::Call* call,
-                                   const MediaConfig& config,
-                                   const VideoOptions& options) {
+  VideoMediaChannel* CreateChannel(
+      webrtc::Call* call,
+      const MediaConfig& config,
+      const VideoOptions& options,
+      const webrtc::CryptoOptions& crypto_options) {
     return nullptr;
   }
 };
diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc
index 8b17ccd..de1a886 100644
--- a/media/engine/webrtcvideoengine.cc
+++ b/media/engine/webrtcvideoengine.cc
@@ -460,10 +460,11 @@
 WebRtcVideoChannel* WebRtcVideoEngine::CreateChannel(
     webrtc::Call* call,
     const MediaConfig& config,
-    const VideoOptions& options) {
+    const VideoOptions& options,
+    const webrtc::CryptoOptions& crypto_options) {
   RTC_LOG(LS_INFO) << "CreateChannel. Options: " << options.ToString();
-  return new WebRtcVideoChannel(call, config, options, encoder_factory_.get(),
-                                decoder_factory_.get());
+  return new WebRtcVideoChannel(call, config, options, crypto_options,
+                                encoder_factory_.get(), decoder_factory_.get());
 }
 
 std::vector<VideoCodec> WebRtcVideoEngine::codecs() const {
@@ -512,6 +513,7 @@
     webrtc::Call* call,
     const MediaConfig& config,
     const VideoOptions& options,
+    const webrtc::CryptoOptions& crypto_options,
     webrtc::VideoEncoderFactory* encoder_factory,
     webrtc::VideoDecoderFactory* decoder_factory)
     : VideoMediaChannel(config),
diff --git a/media/engine/webrtcvideoengine.h b/media/engine/webrtcvideoengine.h
index 27911b6..cf37e45 100644
--- a/media/engine/webrtcvideoengine.h
+++ b/media/engine/webrtcvideoengine.h
@@ -96,9 +96,11 @@
 
   virtual ~WebRtcVideoEngine();
 
-  WebRtcVideoChannel* CreateChannel(webrtc::Call* call,
-                                    const MediaConfig& config,
-                                    const VideoOptions& options);
+  WebRtcVideoChannel* CreateChannel(
+      webrtc::Call* call,
+      const MediaConfig& config,
+      const VideoOptions& options,
+      const webrtc::CryptoOptions& crypto_options);
 
   std::vector<VideoCodec> codecs() const;
   RtpCapabilities GetCapabilities() const;
@@ -113,6 +115,7 @@
   WebRtcVideoChannel(webrtc::Call* call,
                      const MediaConfig& config,
                      const VideoOptions& options,
+                     const webrtc::CryptoOptions& crypto_options,
                      webrtc::VideoEncoderFactory* encoder_factory,
                      webrtc::VideoDecoderFactory* decoder_factory);
   ~WebRtcVideoChannel() override;
diff --git a/media/engine/webrtcvideoengine_unittest.cc b/media/engine/webrtcvideoengine_unittest.cc
index e799b88..ce2bbd9 100644
--- a/media/engine/webrtcvideoengine_unittest.cc
+++ b/media/engine/webrtcvideoengine_unittest.cc
@@ -452,8 +452,8 @@
 TEST_F(WebRtcVideoEngineTest, SetSendFailsBeforeSettingCodecs) {
   encoder_factory_->AddSupportedVideoCodecType("VP8");
 
-  std::unique_ptr<VideoMediaChannel> channel(
-      engine_.CreateChannel(call_.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> channel(engine_.CreateChannel(
+      call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
 
   EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
 
@@ -466,8 +466,8 @@
 TEST_F(WebRtcVideoEngineTest, GetStatsWithoutSendCodecsSetDoesNotCrash) {
   encoder_factory_->AddSupportedVideoCodecType("VP8");
 
-  std::unique_ptr<VideoMediaChannel> channel(
-      engine_.CreateChannel(call_.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> channel(engine_.CreateChannel(
+      call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
   EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
   VideoMediaInfo info;
   channel->GetStats(&info);
@@ -676,8 +676,8 @@
 
 VideoMediaChannel*
 WebRtcVideoEngineTest::SetSendParamsWithAllSupportedCodecs() {
-  VideoMediaChannel* channel =
-      engine_.CreateChannel(call_.get(), GetMediaConfig(), VideoOptions());
+  VideoMediaChannel* channel = engine_.CreateChannel(
+      call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions());
   cricket::VideoSendParameters parameters;
   // We need to look up the codec in the engine to get the correct payload type.
   for (const webrtc::SdpVideoFormat& format :
@@ -696,8 +696,8 @@
 
 VideoMediaChannel* WebRtcVideoEngineTest::SetRecvParamsWithSupportedCodecs(
     const std::vector<VideoCodec>& codecs) {
-  VideoMediaChannel* channel =
-      engine_.CreateChannel(call_.get(), GetMediaConfig(), VideoOptions());
+  VideoMediaChannel* channel = engine_.CreateChannel(
+      call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions());
   cricket::VideoRecvParameters parameters;
   parameters.codecs = codecs;
   EXPECT_TRUE(channel->SetRecvParameters(parameters));
@@ -751,8 +751,8 @@
   EXPECT_EQ(cricket::CS_RUNNING,
             capturer.Start(capturer.GetSupportedFormats()->front()));
 
-  std::unique_ptr<VideoMediaChannel> channel(
-      engine_.CreateChannel(call_.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> channel(engine_.CreateChannel(
+      call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
   cricket::VideoSendParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("H264"));
   EXPECT_TRUE(channel->SetSendParameters(parameters));
@@ -780,8 +780,8 @@
   encoder_factory_->AddSupportedVideoCodecType("VP8");
   encoder_factory_->AddSupportedVideoCodecType("H264");
 
-  std::unique_ptr<VideoMediaChannel> channel(
-      engine_.CreateChannel(call_.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> channel(engine_.CreateChannel(
+      call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
   cricket::VideoSendParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("VP8"));
   EXPECT_TRUE(channel->SetSendParameters(parameters));
@@ -815,8 +815,8 @@
   encoder_factory_->AddSupportedVideoCodecType("VP8");
   encoder_factory_->AddSupportedVideoCodecType("H264");
 
-  std::unique_ptr<VideoMediaChannel> channel(
-      engine_.CreateChannel(call_.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> channel(engine_.CreateChannel(
+      call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
   cricket::VideoSendParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("H264"));
   EXPECT_TRUE(channel->SetSendParameters(parameters));
@@ -847,8 +847,8 @@
       "WebRTC-H264Simulcast/Enabled/");
   encoder_factory_->AddSupportedVideoCodecType("H264");
 
-  std::unique_ptr<VideoMediaChannel> channel(
-      engine_.CreateChannel(call_.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> channel(engine_.CreateChannel(
+      call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
   cricket::VideoSendParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("H264"));
   EXPECT_TRUE(channel->SetSendParameters(parameters));
@@ -1104,8 +1104,8 @@
 
   // Create send channel.
   const int send_ssrc = 123;
-  std::unique_ptr<VideoMediaChannel> send_channel(
-      engine.CreateChannel(call.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> send_channel(engine.CreateChannel(
+      call.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
   cricket::VideoSendParameters send_parameters;
   send_parameters.codecs.push_back(engine_codecs.at(0));
   EXPECT_TRUE(send_channel->SetSendParameters(send_parameters));
@@ -1125,8 +1125,8 @@
 
   // Create recv channel.
   const int recv_ssrc = 321;
-  std::unique_ptr<VideoMediaChannel> recv_channel(
-      engine.CreateChannel(call.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> recv_channel(engine.CreateChannel(
+      call.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
   cricket::VideoRecvParameters recv_parameters;
   recv_parameters.codecs.push_back(engine_codecs.at(0));
   EXPECT_TRUE(recv_channel->SetRecvParameters(recv_parameters));
@@ -1166,8 +1166,8 @@
 
   // Create recv channel.
   const int recv_ssrc = 321;
-  std::unique_ptr<VideoMediaChannel> recv_channel(
-      engine.CreateChannel(call.get(), GetMediaConfig(), VideoOptions()));
+  std::unique_ptr<VideoMediaChannel> recv_channel(engine.CreateChannel(
+      call.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions()));
   cricket::VideoRecvParameters recv_parameters;
   recv_parameters.codecs.push_back(engine.codecs().front());
   EXPECT_TRUE(recv_channel->SetRecvParameters(recv_parameters));
@@ -1256,7 +1256,8 @@
     // frames become flaky.
     media_config.video.enable_cpu_adaptation = false;
     channel_.reset(engine_.CreateChannel(call_.get(), media_config,
-                                         cricket::VideoOptions()));
+                                         cricket::VideoOptions(),
+                                         webrtc::CryptoOptions()));
     channel_->OnReadyToSend(true);
     EXPECT_TRUE(channel_.get() != NULL);
     network_interface_.SetDestination(channel_.get());
@@ -2066,7 +2067,8 @@
 
     fake_call_.reset(new FakeCall());
     channel_.reset(engine_.CreateChannel(fake_call_.get(), GetMediaConfig(),
-                                         VideoOptions()));
+                                         VideoOptions(),
+                                         webrtc::CryptoOptions()));
     channel_->OnReadyToSend(true);
     last_ssrc_ = 123;
     send_parameters_.codecs = engine_.codecs();
@@ -2872,8 +2874,8 @@
   MediaConfig media_config = GetMediaConfig();
   media_config.video.suspend_below_min_bitrate = true;
 
-  channel_.reset(
-      engine_.CreateChannel(fake_call_.get(), media_config, VideoOptions()));
+  channel_.reset(engine_.CreateChannel(
+      fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions()));
   channel_->OnReadyToSend(true);
 
   channel_->SetSendParameters(send_parameters_);
@@ -2882,8 +2884,8 @@
   EXPECT_TRUE(stream->GetConfig().suspend_below_min_bitrate);
 
   media_config.video.suspend_below_min_bitrate = false;
-  channel_.reset(
-      engine_.CreateChannel(fake_call_.get(), media_config, VideoOptions()));
+  channel_.reset(engine_.CreateChannel(
+      fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions()));
   channel_->OnReadyToSend(true);
 
   channel_->SetSendParameters(send_parameters_);
@@ -3223,8 +3225,8 @@
   parameters.codecs.push_back(codec);
 
   MediaConfig media_config = GetMediaConfig();
-  channel_.reset(
-      engine_.CreateChannel(fake_call_.get(), media_config, VideoOptions()));
+  channel_.reset(engine_.CreateChannel(
+      fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions()));
   channel_->OnReadyToSend(true);
   ASSERT_TRUE(channel_->SetSendParameters(parameters));
 
@@ -3304,8 +3306,8 @@
 
   MediaConfig media_config = GetMediaConfig();
   media_config.video.enable_cpu_adaptation = true;
-  channel_.reset(
-      engine_.CreateChannel(fake_call_.get(), media_config, VideoOptions()));
+  channel_.reset(engine_.CreateChannel(
+      fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions()));
   channel_->OnReadyToSend(true);
   ASSERT_TRUE(channel_->SetSendParameters(parameters));
 
@@ -3378,8 +3380,8 @@
 
   MediaConfig media_config = GetMediaConfig();
   media_config.video.enable_cpu_adaptation = true;
-  channel_.reset(
-      engine_.CreateChannel(fake_call_.get(), media_config, VideoOptions()));
+  channel_.reset(engine_.CreateChannel(
+      fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions()));
   channel_->OnReadyToSend(true);
 
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
@@ -3413,8 +3415,8 @@
   if (enable_overuse) {
     media_config.video.enable_cpu_adaptation = true;
   }
-  channel_.reset(
-      engine_.CreateChannel(fake_call_.get(), media_config, VideoOptions()));
+  channel_.reset(engine_.CreateChannel(
+      fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions()));
   channel_->OnReadyToSend(true);
 
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
@@ -4595,15 +4597,15 @@
   MediaConfig config;
   std::unique_ptr<cricket::WebRtcVideoChannel> channel;
 
-  channel.reset(static_cast<cricket::WebRtcVideoChannel*>(
-      engine_.CreateChannel(call_.get(), config, VideoOptions())));
+  channel.reset(static_cast<cricket::WebRtcVideoChannel*>(engine_.CreateChannel(
+      call_.get(), config, VideoOptions(), webrtc::CryptoOptions())));
   channel->SetInterface(network_interface.get());
   // Default value when DSCP is disabled should be DSCP_DEFAULT.
   EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface->dscp());
 
   config.enable_dscp = true;
-  channel.reset(static_cast<cricket::WebRtcVideoChannel*>(
-      engine_.CreateChannel(call_.get(), config, VideoOptions())));
+  channel.reset(static_cast<cricket::WebRtcVideoChannel*>(engine_.CreateChannel(
+      call_.get(), config, VideoOptions(), webrtc::CryptoOptions())));
   channel->SetInterface(network_interface.get());
   EXPECT_EQ(rtc::DSCP_AF41, network_interface->dscp());
 
@@ -4616,8 +4618,8 @@
   // Verify that setting the option to false resets the
   // DiffServCodePoint.
   config.enable_dscp = false;
-  channel.reset(static_cast<cricket::WebRtcVideoChannel*>(
-      engine_.CreateChannel(call_.get(), config, VideoOptions())));
+  channel.reset(static_cast<cricket::WebRtcVideoChannel*>(engine_.CreateChannel(
+      call_.get(), config, VideoOptions(), webrtc::CryptoOptions())));
   channel->SetInterface(network_interface.get());
   EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface->dscp());
 }
@@ -6738,8 +6740,9 @@
 
   void SetUp() override {
     encoder_factory_->AddSupportedVideoCodecType("VP8");
-    channel_.reset(
-        engine_.CreateChannel(&fake_call_, GetMediaConfig(), VideoOptions()));
+    channel_.reset(engine_.CreateChannel(&fake_call_, GetMediaConfig(),
+                                         VideoOptions(),
+                                         webrtc::CryptoOptions()));
     channel_->OnReadyToSend(true);
     last_ssrc_ = 123;
   }
diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc
index bfd0d7e..b71354f 100644
--- a/media/engine/webrtcvoiceengine.cc
+++ b/media/engine/webrtcvoiceengine.cc
@@ -305,9 +305,11 @@
 VoiceMediaChannel* WebRtcVoiceEngine::CreateChannel(
     webrtc::Call* call,
     const MediaConfig& config,
-    const AudioOptions& options) {
+    const AudioOptions& options,
+    const webrtc::CryptoOptions& crypto_options) {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
-  return new WebRtcVoiceMediaChannel(this, config, options, call);
+  return new WebRtcVoiceMediaChannel(this, config, options, crypto_options,
+                                     call);
 }
 
 bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) {
@@ -714,7 +716,8 @@
       webrtc::Transport* send_transport,
       const rtc::scoped_refptr<webrtc::AudioEncoderFactory>& encoder_factory,
       const absl::optional<webrtc::AudioCodecPairId> codec_pair_id,
-      rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor)
+      rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor,
+      const webrtc::CryptoOptions& crypto_options)
       : call_(call),
         config_(send_transport),
         send_side_bwe_with_overhead_(
@@ -732,6 +735,7 @@
     config_.codec_pair_id = codec_pair_id;
     config_.track_id = track_id;
     config_.frame_encryptor = frame_encryptor;
+    config_.crypto_options = crypto_options;
     rtp_parameters_.encodings[0].ssrc = ssrc;
     rtp_parameters_.rtcp.cname = c_name;
     rtp_parameters_.header_extensions = extensions;
@@ -1076,7 +1080,8 @@
       absl::optional<webrtc::AudioCodecPairId> codec_pair_id,
       size_t jitter_buffer_max_packets,
       bool jitter_buffer_fast_accelerate,
-      rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor)
+      rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor,
+      const webrtc::CryptoOptions& crypto_options)
       : call_(call), config_() {
     RTC_DCHECK(call);
     config_.rtp.remote_ssrc = remote_ssrc;
@@ -1094,6 +1099,7 @@
     config_.decoder_map = decoder_map;
     config_.codec_pair_id = codec_pair_id;
     config_.frame_decryptor = frame_decryptor;
+    config_.crypto_options = crypto_options;
     RecreateAudioReceiveStream();
   }
 
@@ -1231,11 +1237,16 @@
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WebRtcAudioReceiveStream);
 };
 
-WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(WebRtcVoiceEngine* engine,
-                                                 const MediaConfig& config,
-                                                 const AudioOptions& options,
-                                                 webrtc::Call* call)
-    : VoiceMediaChannel(config), engine_(engine), call_(call) {
+WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(
+    WebRtcVoiceEngine* engine,
+    const MediaConfig& config,
+    const AudioOptions& options,
+    const webrtc::CryptoOptions& crypto_options,
+    webrtc::Call* call)
+    : VoiceMediaChannel(config),
+      engine_(engine),
+      call_(call),
+      crypto_options_(crypto_options) {
   RTC_LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel";
   RTC_DCHECK(call);
   engine->RegisterChannel(this);
@@ -1757,7 +1768,7 @@
   WebRtcAudioSendStream* stream = new WebRtcAudioSendStream(
       ssrc, mid_, sp.cname, sp.id, send_codec_spec_, send_rtp_extensions_,
       max_send_bitrate_bps_, audio_network_adaptor_config, call_, this,
-      engine()->encoder_factory_, codec_pair_id_, nullptr);
+      engine()->encoder_factory_, codec_pair_id_, nullptr, crypto_options_);
   send_streams_.insert(std::make_pair(ssrc, stream));
 
   // At this point the stream's local SSRC has been updated. If it is the first
@@ -1844,7 +1855,7 @@
                 call_, this, engine()->decoder_factory_, decoder_map_,
                 codec_pair_id_, engine()->audio_jitter_buffer_max_packets_,
                 engine()->audio_jitter_buffer_fast_accelerate_,
-                unsignaled_frame_decryptor_)));
+                unsignaled_frame_decryptor_, crypto_options_)));
   recv_streams_[ssrc]->SetPlayout(playout_);
 
   return true;
diff --git a/media/engine/webrtcvoiceengine.h b/media/engine/webrtcvoiceengine.h
index b39642a..9c013cd 100644
--- a/media/engine/webrtcvoiceengine.h
+++ b/media/engine/webrtcvoiceengine.h
@@ -58,7 +58,8 @@
   rtc::scoped_refptr<webrtc::AudioState> GetAudioState() const;
   VoiceMediaChannel* CreateChannel(webrtc::Call* call,
                                    const MediaConfig& config,
-                                   const AudioOptions& options);
+                                   const AudioOptions& options,
+                                   const webrtc::CryptoOptions& crypto_options);
 
   const std::vector<AudioCodec>& send_codecs() const;
   const std::vector<AudioCodec>& recv_codecs() const;
@@ -142,6 +143,7 @@
   WebRtcVoiceMediaChannel(WebRtcVoiceEngine* engine,
                           const MediaConfig& config,
                           const AudioOptions& options,
+                          const webrtc::CryptoOptions& crypto_options,
                           webrtc::Call* call);
   ~WebRtcVoiceMediaChannel() override;
 
@@ -306,6 +308,9 @@
   const webrtc::AudioCodecPairId codec_pair_id_ =
       webrtc::AudioCodecPairId::Create();
 
+  // Per peer connection crypto options that last for the lifetime of the peer
+  // connection.
+  const webrtc::CryptoOptions crypto_options_;
   // Unsignaled streams have an option to have a frame decryptor set on them.
   rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
       unsignaled_frame_decryptor_;
diff --git a/media/engine/webrtcvoiceengine_unittest.cc b/media/engine/webrtcvoiceengine_unittest.cc
index bb816ee..8d20b72 100644
--- a/media/engine/webrtcvoiceengine_unittest.cc
+++ b/media/engine/webrtcvoiceengine_unittest.cc
@@ -211,7 +211,8 @@
   bool SetupChannel() {
     EXPECT_CALL(*apm_, SetExtraOptions(testing::_));
     channel_ = engine_->CreateChannel(&call_, cricket::MediaConfig(),
-                                      cricket::AudioOptions());
+                                      cricket::AudioOptions(),
+                                      webrtc::CryptoOptions());
     return (channel_ != nullptr);
   }
 
@@ -2915,10 +2916,12 @@
 
   std::unique_ptr<cricket::WebRtcVoiceMediaChannel> channel1(
       static_cast<cricket::WebRtcVoiceMediaChannel*>(engine_->CreateChannel(
-          &call_, cricket::MediaConfig(), cricket::AudioOptions())));
+          &call_, cricket::MediaConfig(), cricket::AudioOptions(),
+          webrtc::CryptoOptions())));
   std::unique_ptr<cricket::WebRtcVoiceMediaChannel> channel2(
       static_cast<cricket::WebRtcVoiceMediaChannel*>(engine_->CreateChannel(
-          &call_, cricket::MediaConfig(), cricket::AudioOptions())));
+          &call_, cricket::MediaConfig(), cricket::AudioOptions(),
+          webrtc::CryptoOptions())));
 
   // Have to add a stream to make SetSend work.
   cricket::StreamParams stream1;
@@ -3025,15 +3028,17 @@
       .WillRepeatedly(SaveArg<0>(&apm_config));
   EXPECT_CALL(*apm_, SetExtraOptions(testing::_)).Times(3);
 
-  channel.reset(static_cast<cricket::WebRtcVoiceMediaChannel*>(
-      engine_->CreateChannel(&call_, config, cricket::AudioOptions())));
+  channel.reset(
+      static_cast<cricket::WebRtcVoiceMediaChannel*>(engine_->CreateChannel(
+          &call_, config, cricket::AudioOptions(), webrtc::CryptoOptions())));
   channel->SetInterface(&network_interface);
   // Default value when DSCP is disabled should be DSCP_DEFAULT.
   EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface.dscp());
 
   config.enable_dscp = true;
-  channel.reset(static_cast<cricket::WebRtcVoiceMediaChannel*>(
-      engine_->CreateChannel(&call_, config, cricket::AudioOptions())));
+  channel.reset(
+      static_cast<cricket::WebRtcVoiceMediaChannel*>(engine_->CreateChannel(
+          &call_, config, cricket::AudioOptions(), webrtc::CryptoOptions())));
   channel->SetInterface(&network_interface);
   EXPECT_EQ(rtc::DSCP_EF, network_interface.dscp());
 
@@ -3045,8 +3050,9 @@
   // Verify that setting the option to false resets the
   // DiffServCodePoint.
   config.enable_dscp = false;
-  channel.reset(static_cast<cricket::WebRtcVoiceMediaChannel*>(
-      engine_->CreateChannel(&call_, config, cricket::AudioOptions())));
+  channel.reset(
+      static_cast<cricket::WebRtcVoiceMediaChannel*>(engine_->CreateChannel(
+          &call_, config, cricket::AudioOptions(), webrtc::CryptoOptions())));
   channel->SetInterface(&network_interface);
   // Default value when DSCP is disabled should be DSCP_DEFAULT.
   EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface.dscp());
@@ -3374,8 +3380,9 @@
   webrtc::RtcEventLogNullImpl event_log;
   std::unique_ptr<webrtc::Call> call(
       webrtc::Call::Create(webrtc::Call::Config(&event_log)));
-  cricket::VoiceMediaChannel* channel = engine.CreateChannel(
-      call.get(), cricket::MediaConfig(), cricket::AudioOptions());
+  cricket::VoiceMediaChannel* channel =
+      engine.CreateChannel(call.get(), cricket::MediaConfig(),
+                           cricket::AudioOptions(), webrtc::CryptoOptions());
   EXPECT_TRUE(channel != nullptr);
   delete channel;
 }
@@ -3397,8 +3404,9 @@
     webrtc::RtcEventLogNullImpl event_log;
     std::unique_ptr<webrtc::Call> call(
         webrtc::Call::Create(webrtc::Call::Config(&event_log)));
-    cricket::VoiceMediaChannel* channel = engine.CreateChannel(
-        call.get(), cricket::MediaConfig(), cricket::AudioOptions());
+    cricket::VoiceMediaChannel* channel =
+        engine.CreateChannel(call.get(), cricket::MediaConfig(),
+                             cricket::AudioOptions(), webrtc::CryptoOptions());
     EXPECT_TRUE(channel != nullptr);
     delete channel;
   }
@@ -3467,8 +3475,9 @@
   cricket::VoiceMediaChannel* channels[32];
   size_t num_channels = 0;
   while (num_channels < arraysize(channels)) {
-    cricket::VoiceMediaChannel* channel = engine.CreateChannel(
-        call.get(), cricket::MediaConfig(), cricket::AudioOptions());
+    cricket::VoiceMediaChannel* channel =
+        engine.CreateChannel(call.get(), cricket::MediaConfig(),
+                             cricket::AudioOptions(), webrtc::CryptoOptions());
     if (!channel)
       break;
     channels[num_channels++] = channel;
@@ -3502,7 +3511,8 @@
   std::unique_ptr<webrtc::Call> call(
       webrtc::Call::Create(webrtc::Call::Config(&event_log)));
   cricket::WebRtcVoiceMediaChannel channel(&engine, cricket::MediaConfig(),
-                                           cricket::AudioOptions(), call.get());
+                                           cricket::AudioOptions(),
+                                           webrtc::CryptoOptions(), call.get());
   cricket::AudioRecvParameters parameters;
   parameters.codecs = engine.recv_codecs();
   EXPECT_TRUE(channel.SetRecvParameters(parameters));
diff --git a/pc/channelmanager.cc b/pc/channelmanager.cc
index 1c80719..3ef9a42 100644
--- a/pc/channelmanager.cc
+++ b/pc/channelmanager.cc
@@ -177,7 +177,7 @@
   }
 
   VoiceMediaChannel* media_channel =
-      media_engine_->CreateChannel(call, media_config, options);
+      media_engine_->CreateChannel(call, media_config, options, crypto_options);
   if (!media_channel) {
     return nullptr;
   }
@@ -243,8 +243,8 @@
     return nullptr;
   }
 
-  VideoMediaChannel* media_channel =
-      media_engine_->CreateVideoChannel(call, media_config, options);
+  VideoMediaChannel* media_channel = media_engine_->CreateVideoChannel(
+      call, media_config, options, crypto_options);
   if (!media_channel) {
     return nullptr;
   }
diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
index 7c67ca3..fdc29bb 100644
--- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java
+++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
@@ -122,9 +122,27 @@
     public int networkIgnoreMask;
     public boolean disableEncryption;
     public boolean disableNetworkMonitor;
+
+    /**
+     * If set to true, the (potentially insecure) crypto cipher SRTP_AES128_CM_SHA1_32
+     * will be included in the list of supported ciphers during negotiation. It will only
+     * be used if both peers support it and no other ciphers get preferred.
+     */
     public boolean enableAes128Sha1_32CryptoCipher;
+
+    /**
+     * Enable GCM crypto suites from RFC 7714 for SRTP. GCM will only be used if both sides enable
+     * it.
+     */
     public boolean enableGcmCryptoSuites;
 
+    /**
+     * If set all RtpSenders must have an FrameEncryptor attached to them before they are allowed to
+     * send packets. All RtpReceivers must have a FrameDecryptor attached to them before they are
+     * able to receive packets.
+     */
+    public boolean requireFrameEncryption;
+
     @CalledByNative("Options")
     int getNetworkIgnoreMask() {
       return networkIgnoreMask;
@@ -149,6 +167,11 @@
     boolean getEnableGcmCryptoSuites() {
       return enableGcmCryptoSuites;
     }
+
+    @CalledByNative("Options")
+    boolean getRequireFrameEncryption() {
+      return requireFrameEncryption;
+    }
   }
 
   public static class Builder {
diff --git a/sdk/android/src/jni/pc/peerconnectionfactory.cc b/sdk/android/src/jni/pc/peerconnectionfactory.cc
index 83591cb..148375e 100644
--- a/sdk/android/src/jni/pc/peerconnectionfactory.cc
+++ b/sdk/android/src/jni/pc/peerconnectionfactory.cc
@@ -54,6 +54,8 @@
       Java_Options_getEnableAes128Sha1_32CryptoCipher(jni, options);
   bool enable_gcm_crypto_suites =
       Java_Options_getEnableGcmCryptoSuites(jni, options);
+  bool require_frame_encryption =
+      Java_Options_getRequireFrameEncryption(jni, options);
 
   PeerConnectionFactoryInterface::Options native_options;
 
@@ -67,6 +69,9 @@
       enable_aes128_sha1_32_crypto_cipher;
   native_options.crypto_options.srtp.enable_gcm_crypto_suites =
       enable_gcm_crypto_suites;
+  native_options.crypto_options.sframe.require_frame_encryption =
+      require_frame_encryption;
+
   return native_options;
 }
 
diff --git a/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.h b/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.h
index af327f9..fb65512 100644
--- a/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.h
+++ b/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.h
@@ -35,6 +35,8 @@
 
 @property(nonatomic, assign) BOOL enableGcmCryptoSuites;
 
+@property(nonatomic, assign) BOOL requireFrameEncryption;
+
 - (instancetype)init NS_DESIGNATED_INITIALIZER;
 
 @end
diff --git a/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.mm b/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.mm
index 1690385..743cb4d 100644
--- a/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.mm
+++ b/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.mm
@@ -36,6 +36,7 @@
 @synthesize ignoreEthernetNetworkAdapter = _ignoreEthernetNetworkAdapter;
 @synthesize enableAes128Sha1_32CryptoCipher = _enableAes128Sha1_32CryptoCipher;
 @synthesize enableGcmCryptoSuites = _enableGcmCryptoSuites;
+@synthesize requireFrameEncryption = _requireFrameEncryption;
 
 - (instancetype)init {
   return [super init];
@@ -55,6 +56,7 @@
   options.crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher =
       self.enableAes128Sha1_32CryptoCipher;
   options.crypto_options.srtp.enable_gcm_crypto_suites = self.enableGcmCryptoSuites;
+  options.crypto_options.sframe.require_frame_encryption = self.requireFrameEncryption;
 
   return options;
 }