158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// found in the LICENSE file.
458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
5d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h"
658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <algorithm>
858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <cerrno>
958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <cstring>
1058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <vector>
1158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/bind.h"
1358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/bind_helpers.h"
1458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/time/time.h"
1558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "chrome/browser/extensions/api/braille_display_private/brlapi_connection.h"
16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.h"
1758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
1858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace extensions {
2058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using content::BrowserThread;
21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using base::Time;
2258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using base::TimeDelta;
2358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace api {
2458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace braille_display_private {
2558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace {
2758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Delay between detecting a directory update and trying to connect
2858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// to the brlapi.
29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)const int64 kConnectionDelayMs = 500;
30d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// How long to periodically retry connecting after a brltty restart.
31d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Some displays are slow to connect.
32d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)const int64 kConnectRetryTimeout = 20000;
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
3458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
3558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)BrailleController::BrailleController() {
3658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
3758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
3858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)BrailleController::~BrailleController() {
3958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
4058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
4158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// static
4258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)BrailleController* BrailleController::GetInstance() {
4358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return BrailleControllerImpl::GetInstance();
4458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
4558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
4658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// static
4758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)BrailleControllerImpl* BrailleControllerImpl::GetInstance() {
4858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return Singleton<BrailleControllerImpl,
4958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   LeakySingletonTraits<BrailleControllerImpl> >::get();
5058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
5158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
5258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)BrailleControllerImpl::BrailleControllerImpl()
53d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    : started_connecting_(false),
54d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      connect_scheduled_(false) {
5558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  create_brlapi_connection_function_ = base::Bind(
5658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      &BrailleControllerImpl::CreateBrlapiConnection,
5758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      base::Unretained(this));
5858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
5958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
6058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)BrailleControllerImpl::~BrailleControllerImpl() {
6158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
6258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
6358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::TryLoadLibBrlApi() {
64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
6558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (libbrlapi_loader_.loaded())
6658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
6758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // These versions of libbrlapi work the same for the functions we
6858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // are using.  (0.6.0 adds brlapi_writeWText).
6958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  static const char* kSupportedVersions[] = {
7058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    "libbrlapi.so.0.5",
7158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    "libbrlapi.so.0.6"
7258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  };
7358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  for (size_t i = 0; i < arraysize(kSupportedVersions); ++i) {
7458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (libbrlapi_loader_.Load(kSupportedVersions[i]))
7558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      return;
7658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
7758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  LOG(WARNING) << "Couldn't load libbrlapi: " << strerror(errno);
7858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
7958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
80d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)scoped_ptr<DisplayState> BrailleControllerImpl::GetDisplayState() {
81effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
82d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  StartConnecting();
83d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  scoped_ptr<DisplayState> display_state(new DisplayState);
8458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (connection_.get() && connection_->Connected()) {
8558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    size_t size;
8658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (!connection_->GetDisplaySize(&size)) {
87d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      Disconnect();
8858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    } else if (size > 0) {  // size == 0 means no display present.
89d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      display_state->available = true;
90d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      display_state->text_cell_count.reset(new int(size));
9158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    }
9258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
93d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return display_state.Pass();
9458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
9558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
9658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::WriteDots(const std::string& cells) {
97effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
9858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (connection_ && connection_->Connected()) {
9958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    size_t size;
10058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (!connection_->GetDisplaySize(&size)) {
101d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      Disconnect();
10258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    }
10358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    std::vector<unsigned char> sizedCells(size);
10458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    std::memcpy(&sizedCells[0], cells.data(), std::min(cells.size(), size));
10558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (size > cells.size())
10658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      std::fill(sizedCells.begin() + cells.size(), sizedCells.end(), 0);
10758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (!connection_->WriteDots(&sizedCells[0]))
108d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      Disconnect();
10958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
11058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
11158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
11258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::AddObserver(BrailleObserver* observer) {
113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::UI);
1141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
1151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                               base::Bind(
1161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                   &BrailleControllerImpl::StartConnecting,
1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                   base::Unretained(this)))) {
1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    NOTREACHED();
1191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
12058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  observers_.AddObserver(observer);
12158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
12258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::RemoveObserver(BrailleObserver* observer) {
124effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::UI);
12558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  observers_.RemoveObserver(observer);
12658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
12758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::SetCreateBrlapiConnectionForTesting(
12958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const CreateBrlapiConnectionFunction& function) {
13058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (function.is_null()) {
13158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    create_brlapi_connection_function_ = base::Bind(
13258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        &BrailleControllerImpl::CreateBrlapiConnection,
13358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        base::Unretained(this));
13458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  } else {
13558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    create_brlapi_connection_function_ = function;
13658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
13758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
13858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
139d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void BrailleControllerImpl::PokeSocketDirForTesting() {
14068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  OnSocketDirChangedOnIOThread();
141d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
142d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
14358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::StartConnecting() {
144effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
145d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (started_connecting_)
146d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
147d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  started_connecting_ = true;
14858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  TryLoadLibBrlApi();
14958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!libbrlapi_loader_.loaded()) {
15058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
15158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
1520f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  // Only try to connect after we've started to watch the
1530f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  // socket directory.  This is necessary to avoid a race condition
1540f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  // and because we don't retry to connect after errors that will
1550f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  // persist until there's a change to the socket directory (i.e.
1560f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  // ENOENT).
1570f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  BrowserThread::PostTaskAndReply(
1580f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      BrowserThread::FILE, FROM_HERE,
1590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      base::Bind(
16068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          &BrailleControllerImpl::StartWatchingSocketDirOnFileThread,
1610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          base::Unretained(this)),
1620f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      base::Bind(
1630f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          &BrailleControllerImpl::TryToConnect,
16468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          base::Unretained(this)));
16568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  ResetRetryConnectHorizon();
16668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
16768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
16868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void BrailleControllerImpl::StartWatchingSocketDirOnFileThread() {
169effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
17058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  base::FilePath brlapi_dir(BRLAPI_SOCKETPATH);
17158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!file_path_watcher_.Watch(
17258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          brlapi_dir, false, base::Bind(
17368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)              &BrailleControllerImpl::OnSocketDirChangedOnFileThread,
17458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)              base::Unretained(this)))) {
17558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    LOG(WARNING) << "Couldn't watch brlapi directory " << BRLAPI_SOCKETPATH;
17658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
17758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
17858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
17968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void BrailleControllerImpl::OnSocketDirChangedOnFileThread(
18068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    const base::FilePath& path, bool error) {
181effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
18258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (error) {
18358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    LOG(ERROR) << "Error watching brlapi directory: " << path.value();
18458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
18558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
18668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  BrowserThread::PostTask(
18768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      BrowserThread::IO, FROM_HERE, base::Bind(
18868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          &BrailleControllerImpl::OnSocketDirChangedOnIOThread,
18968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          base::Unretained(this)));
19068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
19168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
19268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void BrailleControllerImpl::OnSocketDirChangedOnIOThread() {
193effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
1940f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  VLOG(1) << "BrlAPI directory changed";
195d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Every directory change resets the max retry time to the appropriate delay
196d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // into the future.
197d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  ResetRetryConnectHorizon();
198d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Try after an initial delay to give the driver a chance to connect.
199d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  ScheduleTryToConnect();
20058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
20158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
20258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::TryToConnect() {
203effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
20458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DCHECK(libbrlapi_loader_.loaded());
205d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  connect_scheduled_ = false;
20658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!connection_.get())
20758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    connection_ = create_brlapi_connection_function_.Run();
20858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (connection_.get() && !connection_->Connected()) {
2090f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    VLOG(1) << "Trying to connect to brlapi";
2100f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    BrlapiConnection::ConnectResult result = connection_->Connect(base::Bind(
2110f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        &BrailleControllerImpl::DispatchKeys,
2120f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        base::Unretained(this)));
2130f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    switch (result) {
2140f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      case BrlapiConnection::CONNECT_SUCCESS:
2150f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        DispatchOnDisplayStateChanged(GetDisplayState());
2160f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        break;
2170f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      case BrlapiConnection::CONNECT_ERROR_NO_RETRY:
2180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        break;
2190f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      case BrlapiConnection::CONNECT_ERROR_RETRY:
2200f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        ScheduleTryToConnect();
2210f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        break;
2220f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      default:
2230f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        NOTREACHED();
224d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
22558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
22658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
22758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
228d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void BrailleControllerImpl::ResetRetryConnectHorizon() {
229effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
230d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  retry_connect_horizon_ = Time::Now() + TimeDelta::FromMilliseconds(
231d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      kConnectRetryTimeout);
232d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
233d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
234d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void BrailleControllerImpl::ScheduleTryToConnect() {
235effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
236d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  TimeDelta delay(TimeDelta::FromMilliseconds(kConnectionDelayMs));
237d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Don't reschedule if there's already a connect scheduled or
238d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // the next attempt would fall outside of the retry limit.
239d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (connect_scheduled_)
240d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
241d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (Time::Now() + delay > retry_connect_horizon_) {
2420f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    VLOG(1) << "Stopping to retry to connect to brlapi";
243d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
244d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
2450f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  VLOG(1) << "Scheduling connection retry to brlapi";
246d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  connect_scheduled_ = true;
247d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  BrowserThread::PostDelayedTask(BrowserThread::IO, FROM_HERE,
248d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                 base::Bind(
249d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                     &BrailleControllerImpl::TryToConnect,
250d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                     base::Unretained(this)),
251d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                 delay);
252d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
253d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
254d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void BrailleControllerImpl::Disconnect() {
255effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::IO);
256d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (!connection_ || !connection_->Connected())
257d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
258d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  connection_->Disconnect();
259d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DispatchOnDisplayStateChanged(scoped_ptr<DisplayState>(new DisplayState()));
260d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
261d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
26258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)scoped_ptr<BrlapiConnection> BrailleControllerImpl::CreateBrlapiConnection() {
26358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DCHECK(libbrlapi_loader_.loaded());
26458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return BrlapiConnection::Create(&libbrlapi_loader_);
26558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
26658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
26758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::DispatchKeys() {
26858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DCHECK(connection_.get());
26958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  brlapi_keyCode_t code;
27058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  while (true) {
27158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    int result = connection_->ReadKey(&code);
27258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (result < 0) {  // Error.
27358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      brlapi_error_t* err = connection_->BrlapiError();
27458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      if (err->brlerrno == BRLAPI_ERROR_LIBCERR && err->libcerrno == EINTR)
27558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        continue;
27658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      // Disconnect on other errors.
2770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      VLOG(1) << "BrlAPI error: " << connection_->BrlapiStrError();
278d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      Disconnect();
27958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      return;
28058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    } else if (result == 0) { // No more data.
28158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      return;
28258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    }
283cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    scoped_ptr<KeyEvent> event = BrlapiKeyCodeToEvent(code);
28458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (event)
28558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      DispatchKeyEvent(event.Pass());
28658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
28758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
28858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
28958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrailleControllerImpl::DispatchKeyEvent(scoped_ptr<KeyEvent> event) {
29058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
29158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
29258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                            base::Bind(
29358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                &BrailleControllerImpl::DispatchKeyEvent,
29458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                base::Unretained(this),
29558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                base::Passed(&event)));
29658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
29758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  VLOG(1) << "Dispatching key event: " << *event->ToValue();
299010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  FOR_EACH_OBSERVER(BrailleObserver, observers_, OnBrailleKeyEvent(*event));
30058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
30158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
302d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void BrailleControllerImpl::DispatchOnDisplayStateChanged(
303d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    scoped_ptr<DisplayState> new_state) {
304d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
3051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!BrowserThread::PostTask(
3061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)            BrowserThread::UI, FROM_HERE,
3071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)            base::Bind(&BrailleControllerImpl::DispatchOnDisplayStateChanged,
3081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                       base::Unretained(this),
3091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                       base::Passed(&new_state)))) {
3101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      NOTREACHED();
3111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
312d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
313d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
314d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  FOR_EACH_OBSERVER(BrailleObserver, observers_,
315010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    OnBrailleDisplayStateChanged(*new_state));
316d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
317d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
31858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace braille_display_private
31958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace api
32058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace extensions
321