From ca79034a3d23273656903a8e1fc1096bce42f058 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Wed, 22 Feb 2023 13:58:23 -0800 Subject: [PATCH 1/3] Integration tests for renew on license load Merged from https://widevine-internal-review.googlesource.com/164468 We need to add integration tests in the form of duration license tests in order to test that this feature works with licenses from a real server. Bug: 253513745 Test: WV unit/integration tests Change-Id: I926d8309ed24183ae117e3f66fb92fec2d95c310 --- .../cdm/core/test/duration_use_case_test.cpp | 121 +++++++++++++++++- 1 file changed, 117 insertions(+), 4 deletions(-) diff --git a/libwvdrmengine/cdm/core/test/duration_use_case_test.cpp b/libwvdrmengine/cdm/core/test/duration_use_case_test.cpp index 4c56831e..eb19cfe3 100644 --- a/libwvdrmengine/cdm/core/test/duration_use_case_test.cpp +++ b/libwvdrmengine/cdm/core/test/duration_use_case_test.cpp @@ -80,6 +80,8 @@ const RenewalPolicy kLDLRenewal = {"CDM_LimitedDurationLicense_renewal", 0, 0}; const RenewalPolicy kInfiniteRenewal = {"CDM_InfiniteRenewal_renewal", 0, 0}; const RenewalPolicy kLicenseDurationWithRenewal = { "CDM_LicenseDurationWithRenewal_renewal", 10, 0}; +const RenewalPolicy kRenewOnLicenseLoad = {"CDM_RenewOnLicenseLoad_renewal", 0, + 0}; const RenewalPolicy kHeartbeatRenewal = {"CDM_Heartbeat_renewal", 10, 30}; // Key ID in all duration tests. @@ -1549,10 +1551,121 @@ class CdmUseCase_RenewOnLicenseLoad : public RenewalTest { uint64_t renewal_recovery_; }; -// TODO(b/253513745): Replace this with some real tests. -TEST_P(CdmUseCase_RenewOnLicenseLoad, FakeTest) { - FAIL() << "This test will fail on a v18 server, but " - << "should be skipped on all existing servers."; +// License loaded within rental duration window and playback continues. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case1S) { + start_of_playback_ = 10; + SleepUntil(start_of_playback_); + LoadLicense(); + SleepUntilRenewalNeeded(); + const uint64_t start = 15; // time of first decrypt + const uint64_t load_renewal = 20; + const uint64_t stop = 45; + RenewAndContinue(start, load_renewal, stop, kRenewOnLicenseLoad); +} + +// License loaded within rental duration window and playback continues. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case1M) { + start_of_playback_ = 10; + SleepUntil(start_of_playback_); + LoadLicense(); + SleepUntilRenewalNeeded(); + const uint64_t start = 20; // time of first decrypt + const uint64_t load_renewal = 20; + const uint64_t stop = 45; // end of decrypt + RenewAndContinue(start, load_renewal, stop, kRenewOnLicenseLoad); +} + +// License loaded within rental duration window and playback continues. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case1L) { + start_of_playback_ = 10; + SleepUntil(start_of_playback_); + LoadLicense(); + SleepUntilRenewalNeeded(); + RequestRenewal(kRenewOnLicenseLoad); + const uint64_t load_renewal = 20; + const uint64_t start = 25; // time of first decrypt + const uint64_t stop = 45; // end of decrypt + LoadRenewal(load_renewal, kRenewOnLicenseLoad); + AllowPlayback(start, stop); +} + +// License loaded after rental duration window and playback should fail. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case2) { + start_of_playback_ = EndOfRentalWindow() + 1; + ForbidPlayback(start_of_playback_); +} + +// License loaded within rental duration window but renewal not received. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case3S) { + start_of_playback_ = 10; + SleepUntil(start_of_playback_); + LoadLicense(); + const uint64_t start = 15; // time of first decrypt + // Allow playback within the initial renewal window. + const uint64_t cutoff = + start_of_playback_ + renewal_delay_ + renewal_recovery_; + TerminatePlayback(start, cutoff); +} + +// License loaded within rental duration window but renewal not received. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case3M) { + start_of_playback_ = 10; + SleepUntil(start_of_playback_); + LoadLicense(); + const uint64_t start = 25; // time of first decrypt + // Allow playback within the initial renewal window. + const uint64_t cutoff = + start_of_playback_ + renewal_delay_ + renewal_recovery_; + TerminatePlayback(start, cutoff); +} + +// License loaded within rental duration window but renewal not received. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case3L) { + start_of_playback_ = 10; + SleepUntil(start_of_playback_); + LoadLicense(); + const uint64_t start = 35; // time of first decrypt + SleepUntil(start); + // No playback at all. + FailDecrypt(); +} + +// Playback is started within the rental duration window and the renewal is +// received, but playback exceeds the playback duration. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case4) { + start_of_playback_ = 10; + SleepUntil(start_of_playback_); + LoadLicense(); + SleepUntilRenewalNeeded(); + const uint64_t start = 20; // time of first decrypt + const uint64_t load_renewal = 20; + const uint64_t stop = 45; // end of decrypt + const uint64_t cutoff = EndOfPlaybackWindow(); + RenewAndContinue(start, load_renewal, stop, kRenewOnLicenseLoad); + TerminatePlayback(stop, cutoff); +} + +// Playback is able to be restarted. +TEST_P(CdmUseCase_RenewOnLicenseLoad, Case5) { + start_of_playback_ = 10; + const uint64_t load_renewal = 20; + SleepUntil(start_of_playback_); + LoadLicense(); + SleepUntilRenewalNeeded(); + RequestRenewal(kRenewOnLicenseLoad); + const uint64_t start = 20; + const uint64_t end = 35; + RenewAndContinue(start, load_renewal, end, kRenewOnLicenseLoad); + UnloadLicense(); + // Simulate reloading the license, and then reloading the renewal, and then + // restarting playback. That is allowed, and playback shall be terminated at + // the end of the original playback window. + const uint64_t reload_time = 45; + ReloadAndAllowPlayback(reload_time, EndOfPlaybackWindow(), + kRenewOnLicenseLoad); + // But not beyond playback window. + SleepUntil(EndOfPlaybackWindow() + 3 * kFudge); + FailDecrypt(); } // Heartbeat Playback Window License. (See above for notes on Use Case tests). From c41b6cb713bab40c80ecb7505c2070b965f30e43 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Wed, 22 Feb 2023 14:28:46 -0800 Subject: [PATCH 2/3] Request debug headers and log URL correctly Merged from https://widevine-internal-review.googlesource.com/165861 We want debug headers to help diagnose b/186031735. I also saw that we were only logging the domain name for some errors instead of the full URL. Bug: 186031735 Test: GtsMediaTestCases Change-Id: I4d469a73e54f86d4d3b5d50bd0030fdb2a36df50 --- libwvdrmengine/cdm/core/test/http_socket.cpp | 5 ++--- libwvdrmengine/cdm/core/test/http_socket.h | 3 +++ libwvdrmengine/cdm/core/test/url_request.cpp | 16 +++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libwvdrmengine/cdm/core/test/http_socket.cpp b/libwvdrmengine/cdm/core/test/http_socket.cpp index eaff0520..9760a65e 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket.cpp @@ -328,14 +328,13 @@ bool HttpSocket::Connect(int timeout_in_ms) { } else { if (GetError() != ERROR_ASYNC_COMPLETE) { // failed right away. - LOGE("cannot connect to %s, errno = %d", domain_name_.c_str(), - GetError()); + LOGE("cannot connect to %s, errno = %d", url().c_str(), GetError()); CloseSocket(); return false; } else { // in progress. block until timeout expired or connection established. if (!Wait(/* for_read */ false, timeout_in_ms)) { - LOGE("cannot connect to %s", domain_name_.c_str()); + LOGE("cannot connect to %s", url().c_str()); CloseSocket(); return false; } diff --git a/libwvdrmengine/cdm/core/test/http_socket.h b/libwvdrmengine/cdm/core/test/http_socket.h index f32612a1..ae5761d6 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.h +++ b/libwvdrmengine/cdm/core/test/http_socket.h @@ -32,6 +32,9 @@ class HttpSocket { const std::string& domain_name() const { return domain_name_; } int port() const { return atoi(port_.c_str()); } const std::string& resource_path() const { return resource_path_; } + std::string url() const { + return scheme_ + "://" + domain_name_ + ":" + port_ + resource_path_; + } int ReadAndLogErrors(char* data, int len, int timeout_in_ms); int WriteAndLogErrors(const char* data, int len, int timeout_in_ms); diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp index e77c6bf6..860e350d 100644 --- a/libwvdrmengine/cdm/core/test/url_request.cpp +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -94,7 +94,7 @@ void UrlRequest::Reconnect() { is_connected_ = true; } else { LOGE("Failed to connect: url = %s, port = %d, attempt = %u", - socket_.domain_name().c_str(), socket_.port(), i); + socket_.url().c_str(), socket_.port(), i); } } } @@ -121,9 +121,8 @@ bool UrlRequest::GetResponse(std::string* message) { } ConcatenateChunkedResponse(response, message); - LOGV("HTTP response from %s://%s:%d%s: (%zu): %s", socket_.scheme().c_str(), - socket_.domain_name().c_str(), socket_.port(), - socket_.resource_path().c_str(), message->size(), message->c_str()); + LOGV("HTTP response from %s: (%zu): %s", socket_.url().c_str(), + message->size(), message->c_str()); return true; } @@ -131,11 +130,9 @@ void UrlRequest::AssertOkResponse(std::string* message) { ASSERT_TRUE(message); ASSERT_TRUE(GetResponse(message)); const int status_code = GetStatusCode(*message); - ASSERT_EQ(kHttpOk, status_code) - << "HTTP response from " << socket_.scheme() << "://" - << socket_.domain_name() << ":" << socket_.port() - << socket_.resource_path() << ": (" << message->size() << ") :\n" - << *message; + ASSERT_EQ(kHttpOk, status_code) << "HTTP response from " << socket_.url() + << ": (" << message->size() << ") :\n" + << *message; } // static @@ -204,6 +201,7 @@ bool UrlRequest::PostRequestWithPath(const std::string& path, request.append("Connection: close\r\n"); request.append("User-Agent: Widevine CDM v1.0\r\n"); + request.append("X-Return-Encrypted-Headers: request_and_response\r\n"); // buffer to store length of data as a string char data_size_buffer[32] = {0}; From f63a0b287daa424974a22dda2a72159eac68cc99 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Wed, 22 Feb 2023 15:53:42 -0800 Subject: [PATCH 3/3] Remove comment. Merged from https://widevine-internal-review.googlesource.com/164120 Based on review comment in PS10 of http://go/ag/20608670 Bug: 262797186 Test: GtsMediaTestCases Change-Id: Ia54117ffd0882de161e772fc1ca180a8d3f27480 --- libwvdrmengine/include/WVTypes.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libwvdrmengine/include/WVTypes.h b/libwvdrmengine/include/WVTypes.h index 270b194b..0be919c4 100644 --- a/libwvdrmengine/include/WVTypes.h +++ b/libwvdrmengine/include/WVTypes.h @@ -48,7 +48,6 @@ struct WvStatus { private: AidlDrmStatus mStatus{}; wvcdm::CdmResponseType mCdmErr{}; - // WVDRM_DISALLOW_COPY_AND_ASSIGN_AND_NEW(WvStatus); }; } // namespace wvdrm