Do not disable internal keyboard and mouse in docked mode...

...even when the device is in tablet mode position.

Bug: 903526
Change-Id: Ibc6e2cfe547a431ef546ade1b5842d89001a6980
Reviewed-on: https://chromium-review.googlesource.com/c/1422443
Commit-Queue: Xiaoqian Dai <xdai@chromium.org>
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#625245}
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 1f31a73..9fd176e 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -112,6 +112,13 @@
   return TabletModeController::UiMode::kNone;
 }
 
+// Returns true if the device has an active internal display.
+bool HasActiveInternalDisplay() {
+  return display::Display::HasInternalDisplay() &&
+         Shell::Get()->display_manager()->IsActiveDisplayId(
+             display::Display::InternalDisplayId());
+}
+
 }  // namespace
 
 constexpr char TabletModeController::kLidAngleHistogramName[];
@@ -264,9 +271,7 @@
 }
 
 void TabletModeController::OnDisplayConfigurationChanged() {
-  if (!display::Display::HasInternalDisplay() ||
-      !Shell::Get()->display_manager()->IsActiveDisplayId(
-          display::Display::InternalDisplayId())) {
+  if (!HasActiveInternalDisplay()) {
     AttemptLeaveTabletMode();
   } else if (tablet_mode_switch_is_on_ && !IsTabletModeWindowManagerEnabled()) {
     // The internal display has returned, as we are exiting docked mode.
@@ -314,14 +319,9 @@
     return;
   }
 
-  if (!display::Display::HasInternalDisplay())
+  if (!HasActiveInternalDisplay())
     return;
 
-  if (!Shell::Get()->display_manager()->IsActiveDisplayId(
-          display::Display::InternalDisplayId())) {
-    return;
-  }
-
   // Whether or not we enter tablet mode affects whether we handle screen
   // rotation, so determine whether to enter tablet mode first.
   if (update->IsReadingStable(ACCELEROMETER_SOURCE_SCREEN) &&
@@ -358,12 +358,11 @@
   VLOG(1) << "Tablet mode event received: " << static_cast<int>(mode);
   const bool on = mode == chromeos::PowerManagerClient::TabletMode::ON;
   tablet_mode_switch_is_on_ = on;
+
   // Do not change if docked.
-  if (!display::Display::HasInternalDisplay() ||
-      !Shell::Get()->display_manager()->IsActiveDisplayId(
-          display::Display::InternalDisplayId())) {
+  if (!HasActiveInternalDisplay())
     return;
-  }
+
   // The tablet mode switch activates at 300 degrees, so it is always reliable
   // when |on|. However we wish to exit tablet mode at a smaller angle, so
   // when |on| is false we ignore if it is possible to calculate the lid angle.
@@ -640,10 +639,14 @@
     // If we are currently in tablet mode, the internal input events should
     // always be blocked.
     should_block_internal_events = true;
-  } else if (LidAngleIsInTabletModeRange() || tablet_mode_switch_is_on_) {
+  } else if (HasActiveInternalDisplay() &&
+             (LidAngleIsInTabletModeRange() || tablet_mode_switch_is_on_)) {
     // If we are currently in clamshell mode, the intenral input events should
     // only be blocked if the current lid angle belongs to tablet mode angle
     // or |tablet_mode_switch_is_on_| is true.
+    // Note if we don't have an active internal display, the device is currently
+    // in docked mode, and the user may still want to use the internal keyboard
+    // and mouse in docked mode, we don't block internal events in this case.
     should_block_internal_events = true;
   }
 
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
index f26af8f..ff3d941 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
@@ -930,6 +930,48 @@
   EXPECT_TRUE(AreEventsBlocked());
 }
 
+// Test that internal keyboard and mouse are not disabled in docked mode.
+TEST_F(TabletModeControllerTest, InternalKeyboardMouseInDockedModeTest) {
+  UpdateDisplay("800x600, 800x600");
+  const int64_t internal_display_id =
+      display::test::DisplayManagerTestApi(display_manager())
+          .SetFirstDisplayAsInternalDisplay();
+  EXPECT_FALSE(IsTabletModeStarted());
+  // Input devices events are unblocked.
+  EXPECT_FALSE(AreEventsBlocked());
+  EXPECT_TRUE(display::Display::HasInternalDisplay());
+  EXPECT_TRUE(
+      Shell::Get()->display_manager()->IsActiveDisplayId(internal_display_id));
+
+  // Enter tablet mode first.
+  SetTabletMode(true);
+  EXPECT_TRUE(IsTabletModeStarted());
+  EXPECT_TRUE(AreEventsBlocked());
+
+  // Deactivate internal display to simulate Docked Mode.
+  std::vector<display::ManagedDisplayInfo> all_displays;
+  all_displays.push_back(display_manager()->GetDisplayInfo(
+      display_manager()->GetDisplayAt(0).id()));
+  std::vector<display::ManagedDisplayInfo> secondary_only;
+  display::ManagedDisplayInfo secondary_display =
+      display_manager()->GetDisplayInfo(
+          display_manager()->GetDisplayAt(1).id());
+  all_displays.push_back(secondary_display);
+  secondary_only.push_back(secondary_display);
+  display_manager()->OnNativeDisplaysChanged(secondary_only);
+  ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
+  // We should now enter in clamshell mode when the device is docked.
+  EXPECT_FALSE(IsTabletModeStarted());
+  EXPECT_FALSE(AreEventsBlocked());
+
+  // Exiting docked state should enter tablet mode again.
+  display_manager()->OnNativeDisplaysChanged(all_displays);
+  display::test::DisplayManagerTestApi(display_manager())
+      .SetFirstDisplayAsInternalDisplay();
+  EXPECT_TRUE(IsTabletModeStarted());
+  EXPECT_TRUE(AreEventsBlocked());
+}
+
 class TabletModeControllerForceTabletModeTest
     : public TabletModeControllerTest {
  public: