display: Fix analog display content protection
Content protection would be reported as enabled while mirroring to an
external display with an unsecure (e.g. analog) connection type.
Bug: 929449
Test: display_unittests
Change-Id: I4929ff4fa591616f27eaed01875455c86504e91b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1674404
Commit-Queue: Dominik Laskowski <domlaskowski@chromium.org>
Reviewed-by: Daniel Nicoara <dnicoara@chromium.org>
Cr-Commit-Position: refs/heads/master@{#680038}
diff --git a/ui/display/manager/content_protection_manager.cc b/ui/display/manager/content_protection_manager.cc
index 59e8613b..986d961 100644
--- a/ui/display/manager/content_protection_manager.cc
+++ b/ui/display/manager/content_protection_manager.cc
@@ -12,7 +12,6 @@
#include "base/stl_util.h"
#include "ui/display/manager/apply_content_protection_task.h"
#include "ui/display/manager/display_layout_manager.h"
-#include "ui/display/manager/display_util.h"
#include "ui/display/manager/query_content_protection_task.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/types/display_snapshot.h"
@@ -79,10 +78,7 @@
QueryContentProtectionCallback callback) {
DCHECK(disabled() || GetContentProtections(client_id));
- // Exclude virtual displays so that protected content will not be recaptured
- // through the cast stream.
- const DisplaySnapshot* display = GetDisplay(display_id);
- if (disabled() || !display || !IsPhysicalDisplayType(display->type())) {
+ if (disabled() || !GetDisplay(display_id)) {
std::move(callback).Run(/*success=*/false, DISPLAY_CONNECTION_TYPE_NONE,
CONTENT_PROTECTION_METHOD_NONE);
return;
@@ -277,11 +273,6 @@
for (DisplaySnapshot* display : layout_manager_->GetDisplayStates()) {
int64_t display_id = display->display_id();
- if (!IsPhysicalDisplayType(display->type())) {
- NotifyDisplaySecurityObservers(display_id, /*secure=*/false);
- continue;
- }
-
QueueTask(std::make_unique<QueryContentProtectionTask>(
layout_manager_, native_display_delegate_, display_id,
base::BindOnce(&ContentProtectionManager::OnDisplaySecurityQueried,
@@ -300,18 +291,12 @@
(protection_mask != CONTENT_PROTECTION_METHOD_NONE ||
connection_mask == DISPLAY_CONNECTION_TYPE_INTERNAL);
- NotifyDisplaySecurityObservers(display_id, secure);
+ for (Observer& observer : observers_)
+ observer.OnDisplaySecurityChanged(display_id, secure);
}
if (status != Task::Status::KILLED)
DequeueTask();
}
-void ContentProtectionManager::NotifyDisplaySecurityObservers(
- int64_t display_id,
- bool secure) {
- for (Observer& observer : observers_)
- observer.OnDisplaySecurityChanged(display_id, secure);
-}
-
} // namespace display
diff --git a/ui/display/manager/content_protection_manager.h b/ui/display/manager/content_protection_manager.h
index 6364e93..0d4af4cb 100644
--- a/ui/display/manager/content_protection_manager.h
+++ b/ui/display/manager/content_protection_manager.h
@@ -158,7 +158,6 @@
Task::Status status,
uint32_t connection_mask,
uint32_t protection_mask);
- void NotifyDisplaySecurityObservers(int64_t display_id, bool secure);
DisplayLayoutManager* const layout_manager_; // Not owned.
NativeDisplayDelegate* native_display_delegate_ = nullptr; // Not owned.
diff --git a/ui/display/manager/content_protection_manager_unittest.cc b/ui/display/manager/content_protection_manager_unittest.cc
index 0a05e3ce..ee4c0fc 100644
--- a/ui/display/manager/content_protection_manager_unittest.cc
+++ b/ui/display/manager/content_protection_manager_unittest.cc
@@ -18,7 +18,7 @@
namespace {
-constexpr int64_t kDisplayIds[] = {123, 456};
+constexpr int64_t kDisplayIds[] = {123, 456, 789};
const DisplayMode kDisplayMode{gfx::Size(1366, 768), false, 60.0f};
} // namespace
@@ -67,6 +67,12 @@
.SetCurrentMode(kDisplayMode.Clone())
.Build();
+ displays_[2] = FakeDisplaySnapshot::Builder()
+ .SetId(kDisplayIds[2])
+ .SetType(DISPLAY_CONNECTION_TYPE_VGA)
+ .SetCurrentMode(kDisplayMode.Clone())
+ .Build();
+
UpdateDisplays(2);
}
@@ -122,7 +128,7 @@
uint32_t connection_mask_ = DISPLAY_CONNECTION_TYPE_NONE;
uint32_t protection_mask_ = CONTENT_PROTECTION_METHOD_NONE;
- std::unique_ptr<DisplaySnapshot> displays_[2];
+ std::unique_ptr<DisplaySnapshot> displays_[3];
DISALLOW_COPY_AND_ASSIGN(ContentProtectionManagerTest);
};
@@ -674,5 +680,81 @@
observer.security_changes());
}
+TEST_F(ContentProtectionManagerTest, AnalogDisplaySecurity) {
+ UpdateDisplays(3);
+ TestObserver observer(&manager_);
+
+ EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true},
+ {kDisplayIds[1], false},
+ {kDisplayIds[2], false}}),
+ observer.security_changes());
+ observer.Reset();
+
+ auto id = manager_.RegisterClient();
+ EXPECT_TRUE(id);
+
+ native_display_delegate_.set_run_async(true);
+
+ for (int64_t display_id : kDisplayIds) {
+ manager_.ApplyContentProtection(
+ id, display_id, CONTENT_PROTECTION_METHOD_HDCP,
+ base::BindOnce(
+ &ContentProtectionManagerTest::ApplyContentProtectionCallback,
+ base::Unretained(this)));
+ }
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(observer.security_changes().empty());
+
+ EXPECT_TRUE(TriggerDisplaySecurityTimeout());
+ base::RunLoop().RunUntilIdle();
+
+ // Analog display is never secure.
+ EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true},
+ {kDisplayIds[1], true},
+ {kDisplayIds[2], false}}),
+ observer.security_changes());
+ observer.Reset();
+
+ layout_manager_.set_display_state(MULTIPLE_DISPLAY_STATE_MULTI_MIRROR);
+ TriggerDisplayConfiguration();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(TriggerDisplaySecurityTimeout());
+ base::RunLoop().RunUntilIdle();
+
+ // Internal display is not secure if mirrored to an analog display.
+ EXPECT_EQ(SecurityChanges({{kDisplayIds[0], false},
+ {kDisplayIds[1], false},
+ {kDisplayIds[2], false}}),
+ observer.security_changes());
+ observer.Reset();
+
+ layout_manager_.set_display_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED);
+ TriggerDisplayConfiguration();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(TriggerDisplaySecurityTimeout());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true},
+ {kDisplayIds[1], true},
+ {kDisplayIds[2], false}}),
+ observer.security_changes());
+ observer.Reset();
+
+ manager_.UnregisterClient(id);
+
+ // Timer should be stopped when no client requests protection.
+ EXPECT_FALSE(TriggerDisplaySecurityTimeout());
+ base::RunLoop().RunUntilIdle();
+
+ // Observer should be notified when client unregisters.
+ EXPECT_EQ(SecurityChanges({{kDisplayIds[0], true},
+ {kDisplayIds[1], false},
+ {kDisplayIds[2], false}}),
+ observer.security_changes());
+}
+
} // namespace test
} // namespace display
diff --git a/ui/display/manager/query_content_protection_task.cc b/ui/display/manager/query_content_protection_task.cc
index 8488b96..a627324 100644
--- a/ui/display/manager/query_content_protection_task.cc
+++ b/ui/display/manager/query_content_protection_task.cc
@@ -48,8 +48,14 @@
return;
}
+ // Collect displays to be queried based on HDCP capability. For unprotected
+ // displays not inherently secure through an internal connection, record the
+ // existence of an unsecure display to report no protection for all displays
+ // in mirroring mode.
if (protection_mask & CONTENT_PROTECTION_METHOD_HDCP)
hdcp_capable_displays.push_back(display);
+ else if (display->type() != DISPLAY_CONNECTION_TYPE_INTERNAL)
+ no_protection_mask_ |= CONTENT_PROTECTION_METHOD_HDCP;
}
pending_requests_ = hdcp_capable_displays.size();
diff --git a/ui/display/manager/query_content_protection_task_unittest.cc b/ui/display/manager/query_content_protection_task_unittest.cc
index 69b2759c..7404e3b8 100644
--- a/ui/display/manager/query_content_protection_task_unittest.cc
+++ b/ui/display/manager/query_content_protection_task_unittest.cc
@@ -81,7 +81,7 @@
ASSERT_TRUE(response_);
EXPECT_EQ(Status::SUCCESS, response_->status);
EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL, response_->connection_mask);
- EXPECT_EQ(0u, response_->protection_mask);
+ EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, response_->protection_mask);
}
TEST_F(QueryContentProtectionTaskTest, QueryUnknownDisplay) {
@@ -99,7 +99,7 @@
ASSERT_TRUE(response_);
EXPECT_EQ(Status::FAILURE, response_->status);
EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, response_->connection_mask);
- EXPECT_EQ(0u, response_->protection_mask);
+ EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, response_->protection_mask);
}
TEST_F(QueryContentProtectionTaskTest, QueryDisplayThatCannotGetHdcp) {
@@ -135,7 +135,7 @@
ASSERT_TRUE(response_);
EXPECT_EQ(Status::SUCCESS, response_->status);
EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI, response_->connection_mask);
- EXPECT_EQ(0u, response_->protection_mask);
+ EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, response_->protection_mask);
}
TEST_F(QueryContentProtectionTaskTest, QueryDisplayWithHdcpEnabled) {
@@ -173,7 +173,7 @@
ASSERT_TRUE(response_);
EXPECT_EQ(Status::SUCCESS, response_->status);
EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI, response_->connection_mask);
- EXPECT_EQ(0u, response_->protection_mask);
+ EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, response_->protection_mask);
}
TEST_F(QueryContentProtectionTaskTest, QueryInMirroringMode) {
@@ -194,7 +194,63 @@
EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI |
DISPLAY_CONNECTION_TYPE_DVI),
response_->connection_mask);
- EXPECT_EQ(0u, response_->protection_mask);
+ EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, response_->protection_mask);
+}
+
+TEST_F(QueryContentProtectionTaskTest, QueryAnalogDisplay) {
+ std::vector<std::unique_ptr<DisplaySnapshot>> displays;
+ displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_VGA));
+ TestDisplayLayoutManager layout_manager(std::move(displays),
+ MULTIPLE_DISPLAY_STATE_SINGLE);
+
+ QueryContentProtectionTask task(
+ &layout_manager, &display_delegate_, 1,
+ base::Bind(&QueryContentProtectionTaskTest::ResponseCallback,
+ base::Unretained(this)));
+ task.Run();
+
+ ASSERT_TRUE(response_);
+ EXPECT_EQ(Status::SUCCESS, response_->status);
+ EXPECT_EQ(DISPLAY_CONNECTION_TYPE_VGA, response_->connection_mask);
+ EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, response_->protection_mask);
+}
+
+TEST_F(QueryContentProtectionTaskTest, QueryAnalogDisplayMirror) {
+ std::vector<std::unique_ptr<DisplaySnapshot>> displays;
+ displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI));
+ displays.push_back(CreateDisplaySnapshot(2, DISPLAY_CONNECTION_TYPE_VGA));
+ TestDisplayLayoutManager layout_manager(std::move(displays),
+ MULTIPLE_DISPLAY_STATE_MULTI_MIRROR);
+
+ display_delegate_.set_hdcp_state(HDCP_STATE_ENABLED);
+
+ QueryContentProtectionTask task1(
+ &layout_manager, &display_delegate_, 1,
+ base::Bind(&QueryContentProtectionTaskTest::ResponseCallback,
+ base::Unretained(this)));
+ task1.Run();
+
+ ASSERT_TRUE(response_);
+ EXPECT_EQ(Status::SUCCESS, response_->status);
+ EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI |
+ DISPLAY_CONNECTION_TYPE_VGA),
+ response_->connection_mask);
+ EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, response_->protection_mask);
+
+ response_.reset();
+
+ QueryContentProtectionTask task2(
+ &layout_manager, &display_delegate_, 2,
+ base::Bind(&QueryContentProtectionTaskTest::ResponseCallback,
+ base::Unretained(this)));
+ task2.Run();
+
+ ASSERT_TRUE(response_);
+ EXPECT_EQ(Status::SUCCESS, response_->status);
+ EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI |
+ DISPLAY_CONNECTION_TYPE_VGA),
+ response_->connection_mask);
+ EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, response_->protection_mask);
}
} // namespace test