Commit 78dd4267 authored by David Tseng's avatar David Tseng Committed by Commit Bot

BtBrl: add necessary bindings to initialize bluetooth braille

- Adds an extension api, chrome.brailleDisplayPrivate.updateBluetoothBrailleDisplayAddress, which will [re]start brltty with the given device.
- adds a pref to track the braille display desired by a given user
- adds logic to start and stop brltty on launch given a bluetooth braille display

TODO:
- the UI which will do the initial connection/pairing, and unpairing/forgetting of a device

Bug: 882261
Test: manually call all relevant api functions.
Change-Id: Ief0189a3f76ea6518bd8b644d3df816435feb019
- call chrome.brailleDisplayPrivate.updateBluetoothBrailleDisplayAddress
- observe brltty is launched with the given address
- unload and reload chromevox and observe brltty is appropriately loaded/unloaded

Depends on:
https://chromium-review.googlesource.com/c/chromiumos/overlays/chromiumos-overlay/+/1308895

Change-Id: Ief0189a3f76ea6518bd8b644d3df816435feb019
Reviewed-on: https://chromium-review.googlesource.com/c/1308986
Commit-Queue: David Tseng <dtseng@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarRyo Hashimoto <hashimoto@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605185}
parent 4e7c87b5
......@@ -28,6 +28,7 @@
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "base/values.h"
#include "chrome/browser/accessibility/accessibility_extension_api.h"
......@@ -58,6 +59,7 @@
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager_client.h"
#include "chromeos/dbus/upstart_client.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
......@@ -110,6 +112,13 @@ const char kUserSpokenFeedbackEnabled[] = "UserSpokenFeedbackEnabled";
// A key for the startup sound enabled boolean state for a known user.
const char kUserStartupSoundEnabled[] = "UserStartupSoundEnabled";
// A key for the bluetooth braille display for a user.
const char kUserBluetoothBrailleDisplayAddress[] =
"UserBluetoothBrailleDisplayAddress";
// The name of the Brltty upstart job.
constexpr char kBrlttyUpstartJobName[] = "brltty";
static chromeos::AccessibilityManager* g_accessibility_manager = nullptr;
static BrailleController* g_braille_controller_for_test = nullptr;
......@@ -133,6 +142,24 @@ void EnableSwitchAccessAfterChromeVoxMetric(bool val) {
UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosSwitchAccessAfterChromeVox", val);
}
// Restarts (stops, then starts brltty). If |address| is empty, only stops.
// In Upstart, sending an explicit restart command is a no-op if the job isn't
// already started. Without knowledge regarding brltty's current job status,
// stop followed by start ensures we both stop a started job, and also start
// brltty.
void RestartBrltty(const std::string& address) {
chromeos::UpstartClient* client =
chromeos::DBusThreadManager::Get()->GetUpstartClient();
client->StopJob(kBrlttyUpstartJobName, EmptyVoidDBusMethodCallback());
std::vector<std::string> args;
if (address.empty())
return;
args.push_back(base::StringPrintf("ADDRESS=%s", address.c_str()));
client->StartJob(kBrlttyUpstartJobName, args, EmptyVoidDBusMethodCallback());
}
} // namespace
class AccessibilityPanelWidgetObserver : public views::WidgetObserver {
......@@ -1260,6 +1287,11 @@ void AccessibilityManager::PostLoadChromeVox() {
return;
// Do any setup work needed immediately after ChromeVox actually loads.
// Maybe start brltty, if we have a bluetooth device stored for connection.
const std::string& address = GetBluetoothBrailleDisplayAddress();
if (!address.empty())
RestartBrltty(address);
PlayEarcon(SOUND_SPOKEN_FEEDBACK_ENABLED, PlaySoundOption::ALWAYS);
extensions::EventRouter* event_router =
......@@ -1291,6 +1323,10 @@ void AccessibilityManager::PostLoadChromeVox() {
void AccessibilityManager::PostUnloadChromeVox() {
// Do any teardown work needed immediately after ChromeVox actually unloads.
// Stop brltty.
chromeos::DBusThreadManager::Get()->GetUpstartClient()->StopJob(
kBrlttyUpstartJobName, EmptyVoidDBusMethodCallback());
PlayEarcon(SOUND_SPOKEN_FEEDBACK_DISABLED, PlaySoundOption::ALWAYS);
// Clear the accessibility focus ring.
......@@ -1464,6 +1500,35 @@ void AccessibilityManager::SetStartupSoundEnabled(bool value) const {
kUserStartupSoundEnabled, value);
}
const std::string AccessibilityManager::GetBluetoothBrailleDisplayAddress()
const {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
const user_manager::UserList& user_list = user_manager->GetUsers();
if (user_list.empty())
return std::string();
// |user_list| is sorted by last log in date. Take the most recent user to log
// in.
std::string val;
return user_manager::known_user::GetStringPref(
user_list[0]->GetAccountId(), kUserBluetoothBrailleDisplayAddress,
&val)
? val
: std::string();
}
void AccessibilityManager::UpdateBluetoothBrailleDisplayAddress(
const std::string& address) {
CHECK(spoken_feedback_enabled_);
if (!profile_)
return;
user_manager::known_user::SetStringPref(
multi_user_util::GetAccountIdFromProfile(profile_),
kUserBluetoothBrailleDisplayAddress, address);
RestartBrltty(address);
}
void AccessibilityManager::SetProfileForTest(Profile* profile) {
SetProfile(profile);
}
......
......@@ -313,6 +313,12 @@ class AccessibilityManager
// Sets the startup sound user preference.
void SetStartupSoundEnabled(bool value) const;
// Gets the bluetooth braille display device address for the current user.
const std::string GetBluetoothBrailleDisplayAddress() const;
// Sets the bluetooth braille display device address for the current user.
void UpdateBluetoothBrailleDisplayAddress(const std::string& address);
// Test helpers:
void SetProfileForTest(Profile* profile);
static void SetBrailleControllerForTest(
......
......@@ -13,6 +13,7 @@
#include "chrome/browser/profiles/profile_manager.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/browser/chromeos/login/lock/screen_locker.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#endif
......@@ -184,5 +185,20 @@ void BrailleDisplayPrivateWriteDotsFunction::Work() {
bool BrailleDisplayPrivateWriteDotsFunction::Respond() {
return true;
}
ExtensionFunction::ResponseAction
BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction::Run() {
#if !defined(OS_CHROMEOS)
NOTREACHED();
return RespondNow(Error("Unsupported on this platform."));
#else
std::string address;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &address));
chromeos::AccessibilityManager::Get()->UpdateBluetoothBrailleDisplayAddress(
address);
return RespondNow(NoArguments());
#endif
}
} // namespace api
} // namespace extensions
......@@ -106,6 +106,16 @@ class BrailleDisplayPrivateWriteDotsFunction : public AsyncApiFunction {
std::unique_ptr<braille_display_private::WriteDots::Params> params_;
};
class BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction
: public UIThreadExtensionFunction {
~BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction()
override {}
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION(
"brailleDisplayPrivate.updateBluetoothBrailleDisplayAddress",
BRAILLEDISPLAYPRIVATE_UPDATEBLUETOOTHBRAILLEDISPLAYADDRESS)
};
} // namespace api
} // namespace extensions
......
......@@ -74,6 +74,10 @@ namespace brailleDisplayPrivate {
// parameters give the original 2D dimensions of the buffer. To access
// an element cells[r][c], simply access cells[r * columns + c].
static void writeDots(ArrayBuffer cells, long columns, long rows);
// Updates the single user-preferred braille device with the given bluetooth
// device address and starts or restarts the Brltty daemon.
static void updateBluetoothBrailleDisplayAddress(DOMString address);
};
interface Events {
......
......@@ -24,6 +24,12 @@ void FakeUpstartClient::StartJob(const std::string& job,
FROM_HERE, base::BindOnce(std::move(callback), true));
}
void FakeUpstartClient::StopJob(const std::string& job,
VoidDBusMethodCallback callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), true));
}
void FakeUpstartClient::StartAuthPolicyService() {
static_cast<FakeAuthPolicyClient*>(
DBusThreadManager::Get()->GetAuthPolicyClient())
......
......@@ -19,10 +19,12 @@ class CHROMEOS_EXPORT FakeUpstartClient : public UpstartClient {
// DBusClient overrides.
void Init(dbus::Bus* bus) override;
// UpstartClient overrides:
void StartJob(const std::string& job,
const std::vector<std::string>& upstart_env,
VoidDBusMethodCallback callback) override;
// UpstartClient overrides.
void StopJob(const std::string& job,
VoidDBusMethodCallback callback) override;
void StartAuthPolicyService() override;
void RestartAuthPolicyService() override;
void StartMediaAnalytics(const std::vector<std::string>& upstart_env,
......
......@@ -30,13 +30,18 @@ class UpstartClientImpl : public UpstartClient {
~UpstartClientImpl() override = default;
// UpstartClient overrides:
void StartJob(const std::string& job,
const std::vector<std::string>& upstart_env,
VoidDBusMethodCallback callback) override {
CallJobMethod(job, kStartMethod, upstart_env, std::move(callback));
}
// UpstartClient override.
void StopJob(const std::string& job,
VoidDBusMethodCallback callback) override {
CallJobMethod(job, kStopMethod, {}, std::move(callback));
}
void StartAuthPolicyService() override {
StartJob(kAuthPolicyJob, {}, EmptyVoidDBusMethodCallback());
}
......@@ -56,12 +61,11 @@ class UpstartClientImpl : public UpstartClient {
}
void StopMediaAnalytics() override {
CallJobMethod(kMediaAnalyticsJob, kStopMethod, {},
EmptyVoidDBusMethodCallback());
StopJob(kMediaAnalyticsJob, EmptyVoidDBusMethodCallback());
}
void StopMediaAnalytics(VoidDBusMethodCallback callback) override {
CallJobMethod(kMediaAnalyticsJob, kStopMethod, {}, std::move(callback));
StopJob(kMediaAnalyticsJob, std::move(callback));
}
protected:
void Init(dbus::Bus* bus) override {
......
......@@ -31,10 +31,17 @@ class CHROMEOS_EXPORT UpstartClient : public DBusClient {
// |job|: Name of Upstart job.
// |upstart_env|: List of upstart environment variables to be passed to the
// upstart service.
// |callback|: Called with a response.
virtual void StartJob(const std::string& job,
const std::vector<std::string>& upstart_env,
VoidDBusMethodCallback callback) = 0;
// Stops an Upstart job.
// |job|: Name of Upstart job.
// |callback|: Called with a response.
virtual void StopJob(const std::string& job,
VoidDBusMethodCallback callback) = 0;
// Starts authpolicyd.
virtual void StartAuthPolicyService() = 0;
......
......@@ -1351,6 +1351,7 @@ enum HistogramValue {
FILEMANAGERPRIVATEINTERNAL_GETLINUXPACKAGEINFO = 1288,
TABS_GOFORWARD = 1289,
TABS_GOBACK = 1290,
BRAILLEDISPLAYPRIVATE_UPDATEBLUETOOTHBRAILLEDISPLAYADDRESS = 1291,
// Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY
......
......@@ -15742,7 +15742,7 @@ Called by update_net_error_codes.py.-->
<int value="338" label="CAST_CHANNEL_ON_MESSAGE"/>
<int value="339" label="CAST_CHANNEL_ON_ERROR"/>
<int value="340" label="SCREENLOCK_PRIVATE_ON_CHANGED"/>
<int value="341" label="SCREENLOCK_PRIVATE_ON_AUTH_ATTEMPTED"/>
<int value="341" label="DELETED_SCREENLOCK_PRIVATE_ON_AUTH_ATTEMPTED"/>
<int value="342" label="TYPES_CHROME_SETTING_ON_CHANGE"/>
<int value="343"
label="DELETED_TYPES_PRIVATE_CHROME_DIRECT_SETTING_ON_CHANGE"/>
......@@ -17182,6 +17182,8 @@ Called by update_net_error_codes.py.-->
<int value="1288" label="FILEMANAGERPRIVATEINTERNAL_GETLINUXPACKAGEINFO"/>
<int value="1289" label="TABS_GOFORWARD"/>
<int value="1290" label="TABS_GOBACK"/>
<int value="1291"
label="BRAILLEDISPLAYPRIVATE_UPDATEBLUETOOTHBRAILLEDISPLAYADDRESS"/>
</enum>
<enum name="ExtensionIconState">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment