From 67e6a68b14d311eb758ac8a569695a141dcd6627 Mon Sep 17 00:00:00 2001 From: "kcwu@chromium.org" Date: Thu, 31 Oct 2013 10:00:41 +0000 Subject: Only enable HDCP on monitors displaying the protected video BUG=305643 R=derat@chromium.org, dmichael@chromium.org Review URL: https://codereview.chromium.org/49513002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@232075 0039d316-1c4b-4281-b951-d872f2087c98 --- chromeos/display/output_configurator.cc | 114 ++++++++++++++++------- chromeos/display/output_configurator.h | 14 ++- chromeos/display/output_configurator_unittest.cc | 45 ++++++--- 3 files changed, 121 insertions(+), 52 deletions(-) (limited to 'chromeos') diff --git a/chromeos/display/output_configurator.cc b/chromeos/display/output_configurator.cc index 4676246..1a3cc31 100644 --- a/chromeos/display/output_configurator.cc +++ b/chromeos/display/output_configurator.cc @@ -299,21 +299,72 @@ void OutputConfigurator::Start(uint32 background_color_argb) { NotifyObservers(success, new_state); } +bool OutputConfigurator::ApplyProtections(const DisplayProtections& requests) { + for (std::vector::const_iterator it = cached_outputs_.begin(); + it != cached_outputs_.end(); ++it) { + RROutput this_id = it->output; + uint32_t all_desired = 0; + DisplayProtections::const_iterator request_it = requests.find( + it->display_id); + if (request_it != requests.end()) + all_desired = request_it->second; + switch (it->type) { + case OUTPUT_TYPE_UNKNOWN: + return false; + // DisplayPort, DVI, and HDMI all support HDCP. + case OUTPUT_TYPE_DISPLAYPORT: + case OUTPUT_TYPE_DVI: + case OUTPUT_TYPE_HDMI: { + HDCPState new_desired_state = + (all_desired & OUTPUT_PROTECTION_METHOD_HDCP) ? + HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED; + if (!delegate_->SetHDCPState(this_id, new_desired_state)) + return false; + break; + } + case OUTPUT_TYPE_INTERNAL: + case OUTPUT_TYPE_VGA: + case OUTPUT_TYPE_NETWORK: + // No protections for these types. Do nothing. + break; + case OUTPUT_TYPE_NONE: + NOTREACHED(); + break; + } + } + + return true; +} + OutputConfigurator::OutputProtectionClientId OutputConfigurator::RegisterOutputProtectionClient() { if (!configure_display_) - return 0; + return kInvalidClientId; return next_output_protection_client_id_++; } void OutputConfigurator::UnregisterOutputProtectionClient( OutputProtectionClientId client_id) { - EnableOutputProtection(client_id, OUTPUT_PROTECTION_METHOD_NONE); + client_protection_requests_.erase(client_id); + + DisplayProtections protections; + for (ProtectionRequests::const_iterator it = + client_protection_requests_.begin(); + it != client_protection_requests_.end(); + ++it) { + for (DisplayProtections::const_iterator it2 = it->second.begin(); + it2 != it->second.end(); ++it2) { + protections[it2->first] |= it2->second; + } + } + + ApplyProtections(protections); } bool OutputConfigurator::QueryOutputProtectionStatus( OutputProtectionClientId client_id, + int64 display_id, uint32_t* link_mask, uint32_t* protection_mask) { if (!configure_display_) @@ -325,6 +376,8 @@ bool OutputConfigurator::QueryOutputProtectionStatus( for (std::vector::const_iterator it = cached_outputs_.begin(); it != cached_outputs_.end(); ++it) { RROutput this_id = it->output; + if (it->display_id != display_id) + continue; *link_mask |= it->type; switch (it->type) { case OUTPUT_TYPE_UNKNOWN: @@ -356,7 +409,9 @@ bool OutputConfigurator::QueryOutputProtectionStatus( // Don't reveal protections requested by other clients. ProtectionRequests::iterator it = client_protection_requests_.find(client_id); if (it != client_protection_requests_.end()) { - uint32_t requested_mask = it->second; + uint32_t requested_mask = 0; + if (it->second.find(display_id) != it->second.end()) + requested_mask = it->second[display_id]; *protection_mask = enabled & ~unfulfilled & requested_mask; } else { *protection_mask = 0; @@ -366,52 +421,39 @@ bool OutputConfigurator::QueryOutputProtectionStatus( bool OutputConfigurator::EnableOutputProtection( OutputProtectionClientId client_id, + int64 display_id, uint32_t desired_method_mask) { if (!configure_display_) return false; - uint32_t all_desired = desired_method_mask; + DisplayProtections protections; for (ProtectionRequests::const_iterator it = client_protection_requests_.begin(); it != client_protection_requests_.end(); ++it) { - if (it->first != client_id) - all_desired |= it->second; + for (DisplayProtections::const_iterator it2 = it->second.begin(); + it2 != it->second.end(); ++it2) { + if (it->first == client_id && it2->first == display_id) + continue; + protections[it2->first] |= it2->second; + } } + protections[display_id] |= desired_method_mask; - for (std::vector::const_iterator it = cached_outputs_.begin(); - it != cached_outputs_.end(); ++it) { - RROutput this_id = it->output; - switch (it->type) { - case OUTPUT_TYPE_UNKNOWN: - return false; - // DisplayPort, DVI, and HDMI all support HDCP. - case OUTPUT_TYPE_DISPLAYPORT: - case OUTPUT_TYPE_DVI: - case OUTPUT_TYPE_HDMI: { - HDCPState new_desired_state = - (all_desired & OUTPUT_PROTECTION_METHOD_HDCP) ? - HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED; - if (!delegate_->SetHDCPState(this_id, new_desired_state)) - return false; - break; - } - case OUTPUT_TYPE_INTERNAL: - case OUTPUT_TYPE_VGA: - case OUTPUT_TYPE_NETWORK: - // No protections for these types. Do nothing. - break; - case OUTPUT_TYPE_NONE: - NOTREACHED(); - break; + if (!ApplyProtections(protections)) + return false; + + if (desired_method_mask == OUTPUT_PROTECTION_METHOD_NONE) { + if (client_protection_requests_.find(client_id) != + client_protection_requests_.end()) { + client_protection_requests_[client_id].erase(display_id); + if (client_protection_requests_[client_id].size() == 0) + client_protection_requests_.erase(client_id); } + } else { + client_protection_requests_[client_id][display_id] = desired_method_mask; } - if (desired_method_mask == OUTPUT_PROTECTION_METHOD_NONE) - client_protection_requests_.erase(client_id); - else - client_protection_requests_[client_id] = desired_method_mask; - return true; } diff --git a/chromeos/display/output_configurator.h b/chromeos/display/output_configurator.h index 3491344..ef5085e 100644 --- a/chromeos/display/output_configurator.h +++ b/chromeos/display/output_configurator.h @@ -68,6 +68,7 @@ class CHROMEOS_EXPORT OutputConfigurator public base::MessagePumpObserver { public: typedef uint64_t OutputProtectionClientId; + static const OutputProtectionClientId kInvalidClientId = 0; struct ModeInfo { ModeInfo(); @@ -413,6 +414,7 @@ class CHROMEOS_EXPORT OutputConfigurator // Returns true on success. bool QueryOutputProtectionStatus( OutputProtectionClientId client_id, + int64 display_id, uint32_t* link_mask, uint32_t* protection_mask); @@ -422,12 +424,15 @@ class CHROMEOS_EXPORT OutputConfigurator // Returns true when the protection request has been made. bool EnableOutputProtection( OutputProtectionClientId client_id, + int64 display_id, uint32_t desired_protection_mask); private: - // Mapping a client to its protection request bitmask. - typedef std::map ProtectionRequests; + // Mapping a display_id to a protection request bitmask. + typedef std::map DisplayProtections; + // Mapping a client to its protection request. + typedef std::map ProtectionRequests; // Updates |cached_outputs_| to contain currently-connected outputs. Calls // |delegate_->GetOutputs()| and then does additional work, like finding the @@ -487,6 +492,9 @@ class CHROMEOS_EXPORT OutputConfigurator float GetMirroredDisplayAreaRatio( const OutputConfigurator::OutputSnapshot& output); + // Applies output protections according to requests. + bool ApplyProtections(const DisplayProtections& requests); + StateController* state_controller_; SoftwareMirroringController* mirroring_controller_; scoped_ptr delegate_; diff --git a/chromeos/display/output_configurator_unittest.cc b/chromeos/display/output_configurator_unittest.cc index 4e23345..28f0553 100644 --- a/chromeos/display/output_configurator_unittest.cc +++ b/chromeos/display/output_configurator_unittest.cc @@ -358,6 +358,7 @@ class OutputConfiguratorTest : public testing::Test { o->is_aspect_preserving_scaling = true; o->mode_infos[kSmallModeId] = small_mode_info; o->has_display_id = true; + o->display_id = 123; o->index = 0; o = &outputs_[1]; @@ -371,6 +372,7 @@ class OutputConfiguratorTest : public testing::Test { o->mode_infos[kSmallModeId] = small_mode_info; o->mode_infos[kBigModeId] = big_mode_info; o->has_display_id = true; + o->display_id = 456; o->index = 1; UpdateOutputs(2, false); @@ -1120,7 +1122,9 @@ TEST_F(OutputConfiguratorTest, OutputProtection) { EXPECT_NE(kNoActions, delegate_->GetActionsAndClear()); uint32_t link_mask = 0; uint32_t protection_mask = 0; - EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(id, &link_mask, + EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(id, + outputs_[0].display_id, + &link_mask, &protection_mask)); EXPECT_EQ(static_cast(OUTPUT_TYPE_INTERNAL), link_mask); EXPECT_EQ(static_cast(OUTPUT_PROTECTION_METHOD_NONE), @@ -1130,28 +1134,38 @@ TEST_F(OutputConfiguratorTest, OutputProtection) { // Two outputs. UpdateOutputs(2, true); EXPECT_NE(kNoActions, delegate_->GetActionsAndClear()); - EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(id, &link_mask, + EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(id, + outputs_[1].display_id, + &link_mask, &protection_mask)); - EXPECT_EQ(static_cast(OUTPUT_TYPE_INTERNAL | OUTPUT_TYPE_HDMI), + EXPECT_EQ(static_cast(OUTPUT_TYPE_HDMI), link_mask); EXPECT_EQ(static_cast(OUTPUT_PROTECTION_METHOD_NONE), protection_mask); EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); EXPECT_TRUE( - configurator_.EnableOutputProtection(id, OUTPUT_PROTECTION_METHOD_HDCP)); + configurator_.EnableOutputProtection(id, + outputs_[1].display_id, + OUTPUT_PROTECTION_METHOD_HDCP)); EXPECT_EQ(GetSetHDCPStateAction(outputs_[1].output, HDCP_STATE_DESIRED), delegate_->GetActionsAndClear()); // Enable protection. delegate_->set_hdcp_state(HDCP_STATE_ENABLED); - EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(id, &link_mask, + EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(id, + outputs_[1].display_id, + &link_mask, &protection_mask)); - EXPECT_EQ(static_cast(OUTPUT_TYPE_INTERNAL | OUTPUT_TYPE_HDMI), - link_mask); + EXPECT_EQ(static_cast(OUTPUT_TYPE_HDMI), link_mask); EXPECT_EQ(static_cast(OUTPUT_PROTECTION_METHOD_HDCP), protection_mask); EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); + + // Protections should be disabled after unregister. + configurator_.UnregisterOutputProtectionClient(id); + EXPECT_EQ(GetSetHDCPStateAction(outputs_[1].output, HDCP_STATE_UNDESIRED), + delegate_->GetActionsAndClear()); } TEST_F(OutputConfiguratorTest, OutputProtectionTwoClients) { @@ -1169,6 +1183,7 @@ TEST_F(OutputConfiguratorTest, OutputProtectionTwoClients) { // Clients never know state enableness for methods that they didn't request. EXPECT_TRUE( configurator_.EnableOutputProtection(client1, + outputs_[1].display_id, OUTPUT_PROTECTION_METHOD_HDCP)); EXPECT_EQ(GetSetHDCPStateAction(outputs_[1].output, HDCP_STATE_DESIRED).c_str(), @@ -1177,27 +1192,31 @@ TEST_F(OutputConfiguratorTest, OutputProtectionTwoClients) { uint32_t link_mask = 0; uint32_t protection_mask = 0; - EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(client1, &link_mask, + EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(client1, + outputs_[1].display_id, + &link_mask, &protection_mask)); - EXPECT_EQ(static_cast(OUTPUT_TYPE_INTERNAL | OUTPUT_TYPE_HDMI), - link_mask); + EXPECT_EQ(static_cast(OUTPUT_TYPE_HDMI), link_mask); EXPECT_EQ(OUTPUT_PROTECTION_METHOD_HDCP, protection_mask); - EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(client2, &link_mask, + EXPECT_TRUE(configurator_.QueryOutputProtectionStatus(client2, + outputs_[1].display_id, + &link_mask, &protection_mask)); - EXPECT_EQ(static_cast(OUTPUT_TYPE_INTERNAL | OUTPUT_TYPE_HDMI), - link_mask); + EXPECT_EQ(static_cast(OUTPUT_TYPE_HDMI), link_mask); EXPECT_EQ(OUTPUT_PROTECTION_METHOD_NONE, protection_mask); // Protections will be disabled only if no more clients request them. EXPECT_TRUE( configurator_.EnableOutputProtection(client2, + outputs_[1].display_id, OUTPUT_PROTECTION_METHOD_NONE)); EXPECT_EQ(GetSetHDCPStateAction(outputs_[1].output, HDCP_STATE_DESIRED).c_str(), delegate_->GetActionsAndClear()); EXPECT_TRUE( configurator_.EnableOutputProtection(client1, + outputs_[1].display_id, OUTPUT_PROTECTION_METHOD_NONE)); EXPECT_EQ(GetSetHDCPStateAction(outputs_[1].output, HDCP_STATE_UNDESIRED).c_str(), -- cgit v1.1