Notify bluetooth device changes on device added
The BluetoothDevicesObserver notifies its observers when a connected device's configuration changed or when the adapter is powered on/off. This misses the case when a device is added to the adapter, causing folded back ChromeOS devices to boot into tablet mode despite having a bluetooth mouse connected. Added unit tests for TabletModeController to make sure the observer call correctly trigger leaving tablet mode and starting in laptop mode when folded back. Bug: b:362082543 Change-Id: I65356244a1d69dde2391668a7ffce3936bf2c150 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5980092 Reviewed-by: Mitsuru Oshima <oshima@chromium.org> Commit-Queue: Vincent Chiang <vincentchiang@chromium.org> Cr-Commit-Position: refs/heads/main@{#1379798}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
eb8b8cea2e
commit
22c4bacc08
@ -5482,6 +5482,7 @@ static_library("test_support") {
|
||||
"//components/user_manager:user_manager",
|
||||
"//components/viz/test:test_support",
|
||||
"//device/bluetooth",
|
||||
"//device/bluetooth:mocks",
|
||||
"//google_apis/common:test_support",
|
||||
"//services/device/public/cpp:test_support",
|
||||
"//services/device/public/mojom",
|
||||
|
@ -38,6 +38,11 @@ void BluetoothDevicesObserver::AdapterPoweredChanged(
|
||||
adapter_or_device_changed_callback_.Run(/*device=*/nullptr);
|
||||
}
|
||||
|
||||
void BluetoothDevicesObserver::DeviceAdded(device::BluetoothAdapter* adapter,
|
||||
device::BluetoothDevice* device) {
|
||||
adapter_or_device_changed_callback_.Run(device);
|
||||
}
|
||||
|
||||
void BluetoothDevicesObserver::DeviceChanged(device::BluetoothAdapter* adapter,
|
||||
device::BluetoothDevice* device) {
|
||||
adapter_or_device_changed_callback_.Run(device);
|
||||
|
@ -40,6 +40,8 @@ class ASH_EXPORT BluetoothDevicesObserver
|
||||
bool present) override;
|
||||
void AdapterPoweredChanged(device::BluetoothAdapter* adapter,
|
||||
bool powered) override;
|
||||
void DeviceAdded(device::BluetoothAdapter* adapter,
|
||||
device::BluetoothDevice* device) override;
|
||||
void DeviceChanged(device::BluetoothAdapter* adapter,
|
||||
device::BluetoothDevice* device) override;
|
||||
|
||||
|
@ -4,12 +4,16 @@
|
||||
|
||||
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ash/constants/ash_switches.h"
|
||||
#include "ash/shell.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/numerics/math_constants.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/time/default_tick_clock.h"
|
||||
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
|
||||
#include "device/bluetooth/test/mock_bluetooth_device.h"
|
||||
#include "ui/events/devices/device_data_manager_test_api.h"
|
||||
#include "ui/events/devices/input_device.h"
|
||||
#include "ui/events/devices/touchpad_device.h"
|
||||
@ -18,6 +22,8 @@ namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kBluetoothDevicePublicAddress[] = "01:23:45:67:89:AB";
|
||||
|
||||
bool IsTabletModeControllerInitialized() {
|
||||
return base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kAshEnableTabletMode);
|
||||
@ -66,6 +72,42 @@ void TabletModeControllerTestApi::AttachExternalTouchpad() {
|
||||
}
|
||||
}
|
||||
|
||||
void TabletModeControllerTestApi::AttachBluetoothMouse(
|
||||
device::MockBluetoothAdapter* bluetooth_adapter) {
|
||||
uint32_t test_vendor_id = 0x1111;
|
||||
uint32_t test_product_id = 0x1112;
|
||||
const char test_device_name[] = "bluetooth mouse";
|
||||
|
||||
// Need to set device to DeviceDataManager since that's the source of device
|
||||
// look up. This will not trigger tablet mode since it is not yet connected to
|
||||
// bt adapter.
|
||||
ui::DeviceDataManagerTestApi().SetMouseDevices({ui::InputDevice(
|
||||
5, ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH, test_device_name, "",
|
||||
base::FilePath(), test_vendor_id, test_product_id, 0)});
|
||||
|
||||
std::unique_ptr<device::MockBluetoothDevice> mock_device =
|
||||
std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
|
||||
bluetooth_adapter, /*bluetooth_class=*/0, test_device_name,
|
||||
kBluetoothDevicePublicAddress,
|
||||
/*initially_paired=*/true, /*connected=*/true);
|
||||
ON_CALL(*mock_device, GetDeviceType)
|
||||
.WillByDefault(testing::Return(device::BluetoothDeviceType::MOUSE));
|
||||
ON_CALL(*mock_device, GetVendorID)
|
||||
.WillByDefault(testing::Return(test_vendor_id));
|
||||
ON_CALL(*mock_device, GetProductID)
|
||||
.WillByDefault(testing::Return(test_product_id));
|
||||
|
||||
std::vector<raw_ptr<const device::BluetoothDevice, VectorExperimental>>
|
||||
devices;
|
||||
devices.push_back(mock_device.get());
|
||||
ON_CALL(*bluetooth_adapter, GetDevices)
|
||||
.WillByDefault(testing::Return(devices));
|
||||
|
||||
for (auto& observer : bluetooth_adapter->GetObservers()) {
|
||||
observer.DeviceAdded(bluetooth_adapter, mock_device.get());
|
||||
}
|
||||
}
|
||||
|
||||
void TabletModeControllerTestApi::DetachAllMice() {
|
||||
// See comment in |AttachExternalMouse| for why we need to call
|
||||
// |base::RunLoop::RunUntilIdle|.
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "ash/wm/tablet_mode/internal_input_devices_event_blocker.h"
|
||||
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
@ -38,6 +39,7 @@ class TabletModeControllerTestApi {
|
||||
// Called to attach an external mouse/touchpad. If we're currently in tablet
|
||||
// mode, tablet mode will be ended because of this.
|
||||
void AttachExternalMouse();
|
||||
void AttachBluetoothMouse(device::MockBluetoothAdapter* bluetooth_adapter);
|
||||
void AttachExternalTouchpad();
|
||||
|
||||
// Called in association with the above to remove all mice/touchpads.
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "base/test/simple_test_tick_clock.h"
|
||||
#include "chromeos/dbus/power/fake_power_manager_client.h"
|
||||
#include "chromeos/ui/frame/caption_buttons/snap_controller.h"
|
||||
#include "device/bluetooth/bluetooth_adapter_factory.h"
|
||||
#include "ui/aura/client/aura_constants.h"
|
||||
#include "ui/aura/test/test_window_delegate.h"
|
||||
#include "ui/base/hit_test.h"
|
||||
@ -127,6 +128,14 @@ class TabletModeControllerTest : public AshTestBase {
|
||||
void SetUp() override {
|
||||
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
||||
switches::kAshEnableTabletMode);
|
||||
bluetooth_adapter_ =
|
||||
base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>();
|
||||
device::BluetoothAdapterFactory::SetAdapterForTesting(bluetooth_adapter_);
|
||||
ON_CALL(*bluetooth_adapter_, IsPowered)
|
||||
.WillByDefault(testing::Return(true));
|
||||
ON_CALL(*bluetooth_adapter_, IsPresent)
|
||||
.WillByDefault(testing::Return(true));
|
||||
|
||||
AshTestBase::SetUp();
|
||||
AccelerometerReader::GetInstance()->RemoveObserver(
|
||||
tablet_mode_controller());
|
||||
@ -162,7 +171,13 @@ class TabletModeControllerTest : public AshTestBase {
|
||||
}
|
||||
|
||||
void AttachExternalMouse() { test_api_->AttachExternalMouse(); }
|
||||
void AttachBluetoothMouse() {
|
||||
test_api_->AttachBluetoothMouse(bluetooth_adapter_.get());
|
||||
}
|
||||
void AttachExternalTouchpad() { test_api_->AttachExternalTouchpad(); }
|
||||
void ClearBluetoothAdapter() {
|
||||
testing::Mock::VerifyAndClear(bluetooth_adapter_.get());
|
||||
}
|
||||
void DetachAllMice() { test_api_->DetachAllMice(); }
|
||||
void DetachAllTouchpads() { test_api_->DetachAllTouchpads(); }
|
||||
|
||||
@ -251,6 +266,9 @@ class TabletModeControllerTest : public AshTestBase {
|
||||
|
||||
base::SimpleTestTickClock test_tick_clock_;
|
||||
|
||||
scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>>
|
||||
bluetooth_adapter_;
|
||||
|
||||
// Tracks user action counts.
|
||||
base::UserActionTester user_action_tester_;
|
||||
};
|
||||
@ -870,7 +888,7 @@ TEST_F(TabletModeControllerTest, CannotEnterTabletModeWithExternalMouse) {
|
||||
EXPECT_FALSE(display::Screen::GetScreen()->InTabletMode());
|
||||
}
|
||||
|
||||
// Tests that when we plug in a external mouse the device will
|
||||
// Tests that when we plug in an external mouse the device will
|
||||
// leave tablet mode.
|
||||
TEST_F(TabletModeControllerTest, LeaveTabletModeWhenExternalMouseConnected) {
|
||||
// Start in tablet mode.
|
||||
@ -890,6 +908,46 @@ TEST_F(TabletModeControllerTest, LeaveTabletModeWhenExternalMouseConnected) {
|
||||
EXPECT_TRUE(AreEventsBlocked());
|
||||
}
|
||||
|
||||
// Tests that when we connect a bluetooth mouse the device will
|
||||
// leave tablet mode.
|
||||
TEST_F(TabletModeControllerTest, LeaveTabletModeWhenBluetoothMouseConnected) {
|
||||
// Start in tablet mode.
|
||||
OpenLidToAngle(300.0f);
|
||||
EXPECT_TRUE(display::Screen::GetScreen()->InTabletMode());
|
||||
EXPECT_TRUE(AreEventsBlocked());
|
||||
|
||||
// Connect bluetooth mouse and verify that tablet mode has ended, but events
|
||||
// are still blocked because the keyboard is still facing the bottom.
|
||||
AttachBluetoothMouse();
|
||||
EXPECT_FALSE(display::Screen::GetScreen()->InTabletMode());
|
||||
EXPECT_TRUE(AreEventsBlocked());
|
||||
|
||||
// Verify that after disconnecting mouse, tablet mode will resume.
|
||||
ClearBluetoothAdapter();
|
||||
DetachAllMice();
|
||||
EXPECT_TRUE(display::Screen::GetScreen()->InTabletMode());
|
||||
EXPECT_TRUE(AreEventsBlocked());
|
||||
}
|
||||
|
||||
// Tests that when bluetooth mouse is connected device will not start
|
||||
// in tablet mode.
|
||||
TEST_F(TabletModeControllerTest, StartInLaptopModeWhenBluetoothMouseConnected) {
|
||||
// Have a bluetooth mouse connected in the beginning.
|
||||
AttachBluetoothMouse();
|
||||
|
||||
// Start with device folded back and verify that it is not in tablet mode and
|
||||
// events are blocked
|
||||
OpenLidToAngle(300.0f);
|
||||
EXPECT_FALSE(display::Screen::GetScreen()->InTabletMode());
|
||||
EXPECT_TRUE(AreEventsBlocked());
|
||||
|
||||
// Verify that after unplugging the mouse, tablet mode will resume.
|
||||
ClearBluetoothAdapter();
|
||||
DetachAllMice();
|
||||
EXPECT_TRUE(display::Screen::GetScreen()->InTabletMode());
|
||||
EXPECT_TRUE(AreEventsBlocked());
|
||||
}
|
||||
|
||||
// Test that plug in or out a mouse in laptop mode will not change current
|
||||
// laptop mode.
|
||||
TEST_F(TabletModeControllerTest, ExternalMouseInLaptopMode) {
|
||||
|
Reference in New Issue
Block a user