diff options
author | jrummell <jrummell@chromium.org> | 2015-03-17 12:43:27 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-17 19:44:23 +0000 |
commit | 4d83add3219d80f900912f5f5d260f54a7952af1 (patch) | |
tree | 3c6479d27668adbb5dd4cde984006de85057f620 /media | |
parent | 1edacadd43ecfb53d12918dc18854af856c7ad8f (diff) | |
download | chromium_src-4d83add3219d80f900912f5f5d260f54a7952af1.zip chromium_src-4d83add3219d80f900912f5f5d260f54a7952af1.tar.gz chromium_src-4d83add3219d80f900912f5f5d260f54a7952af1.tar.bz2 |
Properly determine if a new key was added in AesDecryptor::Update()
BUG=448219
TEST=new test passes
Review URL: https://codereview.chromium.org/1002193005
Cr-Commit-Position: refs/heads/master@{#320958}
Diffstat (limited to 'media')
-rw-r--r-- | media/cdm/aes_decryptor.cc | 21 | ||||
-rw-r--r-- | media/cdm/aes_decryptor.h | 3 | ||||
-rw-r--r-- | media/cdm/aes_decryptor_unittest.cc | 111 | ||||
-rw-r--r-- | media/test/pipeline_integration_test.cc | 7 |
4 files changed, 95 insertions, 47 deletions
diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc index 1506f7f..2468c74 100644 --- a/media/cdm/aes_decryptor.cc +++ b/media/cdm/aes_decryptor.cc @@ -334,6 +334,7 @@ void AesDecryptor::UpdateSession(const std::string& session_id, return; } + bool key_added = false; for (KeyIdAndKeyPairs::iterator it = keys.begin(); it != keys.end(); ++it) { if (it->second.length() != static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) { @@ -341,6 +342,12 @@ void AesDecryptor::UpdateSession(const std::string& session_id, promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid key length."); return; } + + // If this key_id doesn't currently exist in this session, + // a new key is added. + if (!HasKey(session_id, it->first)) + key_added = true; + if (!AddDecryptionKey(session_id, it->first, it->second)) { promise->reject(INVALID_ACCESS_ERROR, 0, "Unable to add key."); return; @@ -374,9 +381,7 @@ void AesDecryptor::UpdateSession(const std::string& session_id, } } - // Assume that at least 1 new key has been successfully added and thus - // sending true for |has_additional_usable_key|. http://crbug.com/448219. - session_keys_change_cb_.Run(session_id, true, keys_info.Pass()); + session_keys_change_cb_.Run(session_id, key_added, keys_info.Pass()); } void AesDecryptor::CloseSession(const std::string& session_id, @@ -546,6 +551,16 @@ AesDecryptor::DecryptionKey* AesDecryptor::GetKey( return key_id_found->second->LatestDecryptionKey(); } +bool AesDecryptor::HasKey(const std::string& session_id, + const std::string& key_id) { + base::AutoLock auto_lock(key_map_lock_); + KeyIdToSessionKeysMap::const_iterator key_id_found = key_map_.find(key_id); + if (key_id_found == key_map_.end()) + return false; + + return key_id_found->second->Contains(session_id); +} + void AesDecryptor::DeleteKeysForSession(const std::string& session_id) { base::AutoLock auto_lock(key_map_lock_); diff --git a/media/cdm/aes_decryptor.h b/media/cdm/aes_decryptor.h index 99b83db..43c0c88 100644 --- a/media/cdm/aes_decryptor.h +++ b/media/cdm/aes_decryptor.h @@ -126,6 +126,9 @@ class MEDIA_EXPORT AesDecryptor : public MediaKeys, // the key. Returns NULL if no key is associated with |key_id|. DecryptionKey* GetKey(const std::string& key_id) const; + // Determines if |key_id| is already specified for |session_id|. + bool HasKey(const std::string& session_id, const std::string& key_id); + // Deletes all keys associated with |session_id|. void DeleteKeysForSession(const std::string& session_id); diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc index 8ca3940..b8f4990 100644 --- a/media/cdm/aes_decryptor_unittest.cc +++ b/media/cdm/aes_decryptor_unittest.cc @@ -323,11 +323,13 @@ class AesDecryptorTest : public testing::Test { // tests that the update succeeds or generates an error. void UpdateSessionAndExpect(std::string session_id, const std::string& key, - PromiseResult expected_result) { + PromiseResult expected_result, + bool new_key_expected) { DCHECK(!key.empty()); if (expected_result == RESOLVED) { - EXPECT_CALL(*this, OnSessionKeysChangeCalled(session_id, true)); + EXPECT_CALL(*this, + OnSessionKeysChangeCalled(session_id, new_key_expected)); } else { EXPECT_CALL(*this, OnSessionKeysChangeCalled(_, _)).Times(0); } @@ -491,7 +493,7 @@ TEST_F(AesDecryptorTest, CreateSessionWithKeyIdsInitData) { TEST_F(AesDecryptorTest, NormalDecryption) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, iv_, no_subsample_entries_); DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); @@ -506,7 +508,7 @@ TEST_F(AesDecryptorTest, UnencryptedFrame) { TEST_F(AesDecryptorTest, WrongKey) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED, true); scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, iv_, no_subsample_entries_); DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH); @@ -524,29 +526,29 @@ TEST_F(AesDecryptorTest, KeyReplacement) { scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, iv_, no_subsample_entries_); - UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED, true); ASSERT_NO_FATAL_FAILURE(DecryptAndExpect( encrypted_buffer, original_data_, DATA_MISMATCH)); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, false); ASSERT_NO_FATAL_FAILURE( DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); } TEST_F(AesDecryptorTest, WrongSizedKey) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kWrongSizedKeyAsJWK, REJECTED); + UpdateSessionAndExpect(session_id, kWrongSizedKeyAsJWK, REJECTED, true); } TEST_F(AesDecryptorTest, MultipleKeysAndFrames) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, iv_, no_subsample_entries_); ASSERT_NO_FATAL_FAILURE( DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); - UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED, true); // The first key is still available after we added a second key. ASSERT_NO_FATAL_FAILURE( @@ -568,7 +570,7 @@ TEST_F(AesDecryptorTest, MultipleKeysAndFrames) { TEST_F(AesDecryptorTest, CorruptedIv) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); std::vector<uint8> bad_iv = iv_; bad_iv[1]++; @@ -581,7 +583,7 @@ TEST_F(AesDecryptorTest, CorruptedIv) { TEST_F(AesDecryptorTest, CorruptedData) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); std::vector<uint8> bad_data = encrypted_data_; bad_data[1]++; @@ -593,7 +595,7 @@ TEST_F(AesDecryptorTest, CorruptedData) { TEST_F(AesDecryptorTest, EncryptedAsUnencryptedFailure) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, std::vector<uint8>(), no_subsample_entries_); DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH); @@ -601,7 +603,7 @@ TEST_F(AesDecryptorTest, EncryptedAsUnencryptedFailure) { TEST_F(AesDecryptorTest, SubsampleDecryption) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( subsample_encrypted_data_, key_id_, iv_, normal_subsample_entries_); DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); @@ -612,7 +614,7 @@ TEST_F(AesDecryptorTest, SubsampleDecryption) { // disallow such a configuration, it should be covered. TEST_F(AesDecryptorTest, SubsampleDecryptionWithOffset) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( subsample_encrypted_data_, key_id_, iv_, normal_subsample_entries_); DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); @@ -620,7 +622,7 @@ TEST_F(AesDecryptorTest, SubsampleDecryptionWithOffset) { TEST_F(AesDecryptorTest, SubsampleWrongSize) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); std::vector<SubsampleEntry> subsample_entries_wrong_size( kSubsampleEntriesWrongSize, @@ -633,7 +635,7 @@ TEST_F(AesDecryptorTest, SubsampleWrongSize) { TEST_F(AesDecryptorTest, SubsampleInvalidTotalSize) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); std::vector<SubsampleEntry> subsample_entries_invalid_total_size( kSubsampleEntriesInvalidTotalSize, @@ -649,7 +651,7 @@ TEST_F(AesDecryptorTest, SubsampleInvalidTotalSize) { // No cypher bytes in any of the subsamples. TEST_F(AesDecryptorTest, SubsampleClearBytesOnly) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); std::vector<SubsampleEntry> clear_only_subsample_entries( kSubsampleEntriesClearOnly, @@ -663,7 +665,7 @@ TEST_F(AesDecryptorTest, SubsampleClearBytesOnly) { // No clear bytes in any of the subsamples. TEST_F(AesDecryptorTest, SubsampleCypherBytesOnly) { std::string session_id = CreateSession(key_id_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); std::vector<SubsampleEntry> cypher_only_subsample_entries( kSubsampleEntriesCypherOnly, @@ -679,7 +681,7 @@ TEST_F(AesDecryptorTest, CloseSession) { scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, iv_, no_subsample_entries_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); ASSERT_NO_FATAL_FAILURE( DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); @@ -693,7 +695,7 @@ TEST_F(AesDecryptorTest, RemoveSession) { scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, iv_, no_subsample_entries_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); ASSERT_NO_FATAL_FAILURE( DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); @@ -705,7 +707,7 @@ TEST_F(AesDecryptorTest, NoKeyAfterCloseSession) { scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, iv_, no_subsample_entries_); - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); ASSERT_NO_FATAL_FAILURE( DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); @@ -720,13 +722,13 @@ TEST_F(AesDecryptorTest, LatestKeyUsed) { encrypted_data_, key_id_, iv_, no_subsample_entries_); // Add alternate key, buffer should not be decoded properly. - UpdateSessionAndExpect(session_id1, kKeyAlternateAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id1, kKeyAlternateAsJWK, RESOLVED, true); ASSERT_NO_FATAL_FAILURE( DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH)); // Create a second session with a correct key value for key_id_. std::string session_id2 = CreateSession(key_id_); - UpdateSessionAndExpect(session_id2, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id2, kKeyAsJWK, RESOLVED, true); // Should be able to decode with latest key. ASSERT_NO_FATAL_FAILURE( @@ -737,13 +739,13 @@ TEST_F(AesDecryptorTest, LatestKeyUsedAfterCloseSession) { std::string session_id1 = CreateSession(key_id_); scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( encrypted_data_, key_id_, iv_, no_subsample_entries_); - UpdateSessionAndExpect(session_id1, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id1, kKeyAsJWK, RESOLVED, true); ASSERT_NO_FATAL_FAILURE( DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); // Create a second session with a different key value for key_id_. std::string session_id2 = CreateSession(key_id_); - UpdateSessionAndExpect(session_id2, kKeyAlternateAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id2, kKeyAlternateAsJWK, RESOLVED, true); // Should not be able to decode with new key. ASSERT_NO_FATAL_FAILURE( @@ -766,7 +768,7 @@ TEST_F(AesDecryptorTest, JWKKey) { " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\"," " \"k\": \"FBUWFxgZGhscHR4fICEiIw\"" "}"; - UpdateSessionAndExpect(session_id, kJwkSimple, REJECTED); + UpdateSessionAndExpect(session_id, kJwkSimple, REJECTED, true); // Try a key list with multiple entries. const std::string kJwksMultipleEntries = @@ -786,38 +788,40 @@ TEST_F(AesDecryptorTest, JWKKey) { " }" " ]" "}"; - UpdateSessionAndExpect(session_id, kJwksMultipleEntries, RESOLVED); + UpdateSessionAndExpect(session_id, kJwksMultipleEntries, RESOLVED, true); // Try a key with no spaces and some \n plus additional fields. const std::string kJwksNoSpaces = "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\"," - "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"GawgguFyGrWKav7AX4VKUg" + "\"kid\":\"AQIDBAUGBwgJCgsMCg4PAA\",\"k\":\"GawgguFyGrWKav7AX4VKUg" "\",\"foo\":\"bar\"}]}\n\n"; - UpdateSessionAndExpect(session_id, kJwksNoSpaces, RESOLVED); + UpdateSessionAndExpect(session_id, kJwksNoSpaces, RESOLVED, true); // Try some non-ASCII characters. - UpdateSessionAndExpect( - session_id, "This is not ASCII due to \xff\xfe\xfd in it.", REJECTED); + UpdateSessionAndExpect(session_id, + "This is not ASCII due to \xff\xfe\xfd in it.", + REJECTED, true); // Try a badly formatted key. Assume that the JSON parser is fully tested, // so we won't try a lot of combinations. However, need a test to ensure // that the code doesn't crash if invalid JSON received. - UpdateSessionAndExpect(session_id, "This is not a JSON key.", REJECTED); + UpdateSessionAndExpect(session_id, "This is not a JSON key.", REJECTED, true); // Try passing some valid JSON that is not a dictionary at the top level. - UpdateSessionAndExpect(session_id, "40", REJECTED); + UpdateSessionAndExpect(session_id, "40", REJECTED, true); // Try an empty dictionary. - UpdateSessionAndExpect(session_id, "{ }", REJECTED); + UpdateSessionAndExpect(session_id, "{ }", REJECTED, true); // Try an empty 'keys' dictionary. - UpdateSessionAndExpect(session_id, "{ \"keys\": [] }", REJECTED); + UpdateSessionAndExpect(session_id, "{ \"keys\": [] }", REJECTED, true); // Try with 'keys' not a dictionary. - UpdateSessionAndExpect(session_id, "{ \"keys\":\"1\" }", REJECTED); + UpdateSessionAndExpect(session_id, "{ \"keys\":\"1\" }", REJECTED, true); // Try with 'keys' a list of integers. - UpdateSessionAndExpect(session_id, "{ \"keys\": [ 1, 2, 3 ] }", REJECTED); + UpdateSessionAndExpect(session_id, "{ \"keys\": [ 1, 2, 3 ] }", REJECTED, + true); // Try padding(=) at end of 'k' base64 string. const std::string kJwksWithPaddedKey = @@ -831,7 +835,7 @@ TEST_F(AesDecryptorTest, JWKKey) { " }" " ]" "}"; - UpdateSessionAndExpect(session_id, kJwksWithPaddedKey, REJECTED); + UpdateSessionAndExpect(session_id, kJwksWithPaddedKey, REJECTED, true); // Try padding(=) at end of 'kid' base64 string. const std::string kJwksWithPaddedKeyId = @@ -845,7 +849,7 @@ TEST_F(AesDecryptorTest, JWKKey) { " }" " ]" "}"; - UpdateSessionAndExpect(session_id, kJwksWithPaddedKeyId, REJECTED); + UpdateSessionAndExpect(session_id, kJwksWithPaddedKeyId, REJECTED, true); // Try a key with invalid base64 encoding. const std::string kJwksWithInvalidBase64 = @@ -859,7 +863,7 @@ TEST_F(AesDecryptorTest, JWKKey) { " }" " ]" "}"; - UpdateSessionAndExpect(session_id, kJwksWithInvalidBase64, REJECTED); + UpdateSessionAndExpect(session_id, kJwksWithInvalidBase64, REJECTED, true); // Try a 3-byte 'kid' where no base64 padding is required. // |kJwksMultipleEntries| above has 2 'kid's that require 1 and 2 padding @@ -875,7 +879,7 @@ TEST_F(AesDecryptorTest, JWKKey) { " }" " ]" "}"; - UpdateSessionAndExpect(session_id, kJwksWithNoPadding, RESOLVED); + UpdateSessionAndExpect(session_id, kJwksWithNoPadding, RESOLVED, true); // Empty key id. const std::string kJwksWithEmptyKeyId = @@ -889,7 +893,7 @@ TEST_F(AesDecryptorTest, JWKKey) { " }" " ]" "}"; - UpdateSessionAndExpect(session_id, kJwksWithEmptyKeyId, REJECTED); + UpdateSessionAndExpect(session_id, kJwksWithEmptyKeyId, REJECTED, true); CloseSession(session_id); } @@ -902,14 +906,33 @@ TEST_F(AesDecryptorTest, GetKeyIds) { EXPECT_FALSE(KeysInfoContains(key_id2)); // Add 1 key, verify it is returned. - UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); EXPECT_TRUE(KeysInfoContains(key_id1)); EXPECT_FALSE(KeysInfoContains(key_id2)); // Add second key, verify both IDs returned. - UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED); + UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED, true); EXPECT_TRUE(KeysInfoContains(key_id1)); EXPECT_TRUE(KeysInfoContains(key_id2)); } +TEST_F(AesDecryptorTest, NoKeysChangeForSameKey) { + std::vector<uint8> key_id(kKeyId, kKeyId + arraysize(kKeyId)); + + std::string session_id = CreateSession(key_id_); + EXPECT_FALSE(KeysInfoContains(key_id)); + + // Add key, verify it is returned. + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true); + EXPECT_TRUE(KeysInfoContains(key_id)); + + // Add key a second time. + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, false); + EXPECT_TRUE(KeysInfoContains(key_id)); + + // Create a new session. Add key, should indicate key added for this session. + std::string session_id2 = CreateSession(key_id_); + UpdateSessionAndExpect(session_id2, kKeyAsJWK, RESOLVED, true); +} + } // namespace media diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc index bf502df..5e546a8 100644 --- a/media/test/pipeline_integration_test.cc +++ b/media/test/pipeline_integration_test.cc @@ -302,6 +302,12 @@ class KeyProvidingApp : public FakeEncryptedMedia::AppBase { void OnEncryptedMediaInitData(const std::string& init_data_type, const std::vector<uint8>& init_data, AesDecryptor* decryptor) override { + // Since only 1 session is created, skip the request if the |init_data| + // has been seen before (no need to add the same key again). + if (init_data == prev_init_data_) + return; + prev_init_data_ = init_data; + if (current_session_id_.empty()) { if (init_data_type == kCencInitDataType) { // Since the 'cenc' files are not created with proper 'pssh' boxes, @@ -340,6 +346,7 @@ class KeyProvidingApp : public FakeEncryptedMedia::AppBase { } std::string current_session_id_; + std::vector<uint8> prev_init_data_; }; class RotatingKeyProvidingApp : public KeyProvidingApp { |