OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos/ime/ibus_daemon_controller.h" | 5 #include "chromeos/ime/ibus_daemon_controller.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/environment.h" | 8 #include "base/files/file_path.h" |
9 #include "base/files/file_path_watcher.h" | 9 #include "base/files/file_path_watcher.h" |
10 #include "base/location.h" | 10 #include "base/location.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/observer_list.h" | 12 #include "base/observer_list.h" |
13 #include "base/process/launch.h" | |
14 #include "base/process/process_handle.h" | |
15 #include "base/rand_util.h" | |
16 #include "base/strings/string_util.h" | |
17 #include "base/strings/stringprintf.h" | |
18 #include "base/threading/thread_checker.h" | |
19 #include "chromeos/dbus/dbus_thread_manager.h" | 13 #include "chromeos/dbus/dbus_thread_manager.h" |
20 | 14 |
21 namespace chromeos { | 15 namespace chromeos { |
22 | 16 |
23 namespace { | 17 namespace { |
24 | 18 |
25 IBusDaemonController* g_ibus_daemon_controller = NULL; | 19 IBusDaemonController* g_ibus_daemon_controller = NULL; |
26 base::FilePathWatcher* g_file_path_watcher = NULL; | 20 base::FilePathWatcher* g_file_path_watcher = NULL; |
27 | 21 |
28 // Called when the ibus-daemon address file is modified. | 22 // Called when the ibus-daemon address file is modified. |
(...skipping 23 matching lines...) Expand all Loading... |
52 g_file_path_watcher = new base::FilePathWatcher; | 46 g_file_path_watcher = new base::FilePathWatcher; |
53 bool result = g_file_path_watcher->Watch( | 47 bool result = g_file_path_watcher->Watch( |
54 base::FilePath::FromUTF8Unsafe(address_file_path), | 48 base::FilePath::FromUTF8Unsafe(address_file_path), |
55 false, // do not watch child directory. | 49 false, // do not watch child directory. |
56 base::Bind(&OnFilePathChanged, | 50 base::Bind(&OnFilePathChanged, |
57 ui_task_runner, | 51 ui_task_runner, |
58 closure)); | 52 closure)); |
59 DCHECK(result); | 53 DCHECK(result); |
60 } | 54 } |
61 | 55 |
62 // The implementation of IBusDaemonController. | |
63 class IBusDaemonControllerImpl : public IBusDaemonController { | |
64 public: | |
65 // Represents current ibus-daemon status. | |
66 enum IBusDaemonStatus { | |
67 IBUS_DAEMON_INITIALIZING, | |
68 IBUS_DAEMON_RUNNING, | |
69 IBUS_DAEMON_SHUTTING_DOWN, | |
70 IBUS_DAEMON_STOP, | |
71 }; | |
72 | |
73 IBusDaemonControllerImpl( | |
74 const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner, | |
75 const scoped_refptr<base::SequencedTaskRunner>& file_task_runner) | |
76 : process_handle_(base::kNullProcessHandle), | |
77 ibus_daemon_status_(IBUS_DAEMON_STOP), | |
78 ui_task_runner_(ui_task_runner), | |
79 file_task_runner_(file_task_runner), | |
80 weak_ptr_factory_(this) { | |
81 } | |
82 | |
83 virtual ~IBusDaemonControllerImpl() {} | |
84 | |
85 // IBusDaemonController override: | |
86 virtual void AddObserver(Observer* observer) OVERRIDE { | |
87 DCHECK(thread_checker_.CalledOnValidThread()); | |
88 observers_.AddObserver(observer); | |
89 } | |
90 | |
91 // IBusDaemonController override: | |
92 virtual void RemoveObserver(Observer* observer) OVERRIDE { | |
93 DCHECK(thread_checker_.CalledOnValidThread()); | |
94 observers_.RemoveObserver(observer); | |
95 } | |
96 | |
97 // IBusDaemonController override: | |
98 virtual bool Start() OVERRIDE { | |
99 DCHECK(thread_checker_.CalledOnValidThread()); | |
100 if (ibus_daemon_status_ == IBUS_DAEMON_RUNNING) | |
101 return true; | |
102 if (ibus_daemon_status_ == IBUS_DAEMON_STOP || | |
103 ibus_daemon_status_ == IBUS_DAEMON_SHUTTING_DOWN) { | |
104 return StartIBusDaemon(); | |
105 } | |
106 return true; | |
107 } | |
108 | |
109 // IBusDaemonController override: | |
110 virtual bool Stop() OVERRIDE { | |
111 DCHECK(thread_checker_.CalledOnValidThread()); | |
112 NOTREACHED() << "Termination of ibus-daemon is not supported" | |
113 << "http://crosbug.com/27051"; | |
114 return false; | |
115 } | |
116 | |
117 private: | |
118 // Starts ibus-daemon service. | |
119 bool StartIBusDaemon() { | |
120 if (ibus_daemon_status_ == IBUS_DAEMON_INITIALIZING || | |
121 ibus_daemon_status_ == IBUS_DAEMON_RUNNING) { | |
122 DVLOG(1) << "MaybeLaunchIBusDaemon: ibus-daemon is already running."; | |
123 return false; | |
124 } | |
125 | |
126 ibus_daemon_status_ = IBUS_DAEMON_INITIALIZING; | |
127 ibus_daemon_address_ = base::StringPrintf( | |
128 "unix:abstract=ibus-%d", | |
129 base::RandInt(0, std::numeric_limits<int>::max())); | |
130 | |
131 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
132 std::string address_file_path; | |
133 env->GetVar("IBUS_ADDRESS_FILE", &address_file_path); | |
134 DCHECK(!address_file_path.empty()); | |
135 | |
136 // Set up ibus-daemon address file watcher before launching ibus-daemon, | |
137 // because if watcher starts after ibus-daemon, we may miss the ibus | |
138 // connection initialization. | |
139 bool success = file_task_runner_->PostTaskAndReply( | |
140 FROM_HERE, | |
141 base::Bind(&StartWatch, | |
142 address_file_path, | |
143 base::Bind(&IBusDaemonControllerImpl::FilePathChanged, | |
144 weak_ptr_factory_.GetWeakPtr(), | |
145 ibus_daemon_address_), | |
146 ui_task_runner_), | |
147 base::Bind(&IBusDaemonControllerImpl::LaunchIBusDaemon, | |
148 weak_ptr_factory_.GetWeakPtr(), | |
149 ibus_daemon_address_)); | |
150 DCHECK(success); | |
151 return true; | |
152 } | |
153 | |
154 // Launhes actual ibus-daemon process. | |
155 void LaunchIBusDaemon(const std::string& ibus_address) { | |
156 DCHECK(thread_checker_.CalledOnValidThread()); | |
157 DCHECK_EQ(base::kNullProcessHandle, process_handle_); | |
158 static const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon"; | |
159 // TODO(zork): Send output to /var/log/ibus.log | |
160 std::vector<std::string> ibus_daemon_command_line; | |
161 ibus_daemon_command_line.push_back(kIBusDaemonPath); | |
162 ibus_daemon_command_line.push_back("--panel=disable"); | |
163 ibus_daemon_command_line.push_back("--cache=none"); | |
164 ibus_daemon_command_line.push_back("--restart"); | |
165 ibus_daemon_command_line.push_back("--replace"); | |
166 ibus_daemon_command_line.push_back("--address=" + ibus_address); | |
167 | |
168 if (!base::LaunchProcess(ibus_daemon_command_line, | |
169 base::LaunchOptions(), | |
170 &process_handle_)) { | |
171 LOG(WARNING) << "Could not launch: " | |
172 << JoinString(ibus_daemon_command_line, " "); | |
173 } | |
174 } | |
175 | |
176 // Called by FilePathWatcher when the ibus-daemon address file is changed. | |
177 // This function will be called on FILE thread. | |
178 void FilePathChanged(const std::string& ibus_address) { | |
179 ui_task_runner_->PostTask( | |
180 FROM_HERE, | |
181 base::Bind(&IBusDaemonControllerImpl::IBusDaemonInitializationDone, | |
182 weak_ptr_factory_.GetWeakPtr(), | |
183 ibus_address)); | |
184 } | |
185 | |
186 // Called by FilePathChaged function, this function should be called on UI | |
187 // thread. | |
188 void IBusDaemonInitializationDone(const std::string& ibus_address) { | |
189 if (ibus_daemon_address_ != ibus_address) | |
190 return; | |
191 | |
192 if (ibus_daemon_status_ != IBUS_DAEMON_INITIALIZING) { | |
193 // Stop() or OnIBusDaemonExit() has already been called. | |
194 return; | |
195 } | |
196 | |
197 DBusThreadManager::Get()->InitIBusBus( | |
198 ibus_address, | |
199 base::Bind(&IBusDaemonControllerImpl::OnIBusDaemonDisconnected, | |
200 weak_ptr_factory_.GetWeakPtr(), | |
201 base::GetProcId(process_handle_))); | |
202 ibus_daemon_status_ = IBUS_DAEMON_RUNNING; | |
203 FOR_EACH_OBSERVER(Observer, observers_, OnConnected()); | |
204 | |
205 VLOG(1) << "The ibus-daemon initialization is done."; | |
206 } | |
207 | |
208 // Called when the connection with ibus-daemon is disconnected. | |
209 void OnIBusDaemonDisconnected(base::ProcessId pid) { | |
210 if (!chromeos::DBusThreadManager::Get()) | |
211 return; // Expected disconnection at shutting down. do nothing. | |
212 | |
213 if (process_handle_ != base::kNullProcessHandle) { | |
214 if (base::GetProcId(process_handle_) == pid) { | |
215 // ibus-daemon crashed. | |
216 // TODO(nona): Shutdown ibus-bus connection. | |
217 process_handle_ = base::kNullProcessHandle; | |
218 } else { | |
219 // This condition is as follows. | |
220 // 1. Called Stop (process_handle_ becomes null) | |
221 // 2. Called LaunchProcess (process_handle_ becomes new instance) | |
222 // 3. Callbacked OnIBusDaemonExit for old instance and reach here. | |
223 // In this case, we should not reset process_handle_ as null, and do not | |
224 // re-launch ibus-daemon. | |
225 return; | |
226 } | |
227 } | |
228 | |
229 const IBusDaemonStatus on_exit_state = ibus_daemon_status_; | |
230 ibus_daemon_status_ = IBUS_DAEMON_STOP; | |
231 FOR_EACH_OBSERVER(Observer, observers_, OnDisconnected()); | |
232 | |
233 if (on_exit_state == IBUS_DAEMON_SHUTTING_DOWN) | |
234 return; // Normal exitting, so do nothing. | |
235 | |
236 LOG(ERROR) << "The ibus-daemon crashed. Re-launching..."; | |
237 StartIBusDaemon(); | |
238 } | |
239 | |
240 // The current ibus_daemon address. This value is assigned at the launching | |
241 // ibus-daemon and used in bus connection initialization. | |
242 std::string ibus_daemon_address_; | |
243 | |
244 // The process handle of the IBus daemon. kNullProcessHandle if it's not | |
245 // running. | |
246 base::ProcessHandle process_handle_; | |
247 | |
248 // Represents ibus-daemon's status. | |
249 IBusDaemonStatus ibus_daemon_status_; | |
250 | |
251 // The task runner of UI thread. | |
252 scoped_refptr<base::SequencedTaskRunner> ui_task_runner_; | |
253 | |
254 // The task runner of FILE thread. | |
255 scoped_refptr<base::SequencedTaskRunner> file_task_runner_; | |
256 | |
257 ObserverList<Observer> observers_; | |
258 base::ThreadChecker thread_checker_; | |
259 | |
260 // Used for making callbacks for PostTask. | |
261 base::WeakPtrFactory<IBusDaemonControllerImpl> weak_ptr_factory_; | |
262 | |
263 DISALLOW_COPY_AND_ASSIGN(IBusDaemonControllerImpl); | |
264 }; | |
265 | |
266 // An implementation of IBusDaemonController without ibus-daemon interaction. | 56 // An implementation of IBusDaemonController without ibus-daemon interaction. |
267 // Currently this class is used only on linux desktop. | 57 // Currently this class is used only on linux desktop. |
268 // TODO(nona): Remove IBusDaemonControlelr this once crbug.com/171351 is fixed. | 58 // TODO(nona): Remove IBusDaemonControlelr this once crbug.com/171351 is fixed. |
269 class IBusDaemonControllerDaemonlessImpl : public IBusDaemonController { | 59 class IBusDaemonControllerDaemonlessImpl : public IBusDaemonController { |
270 public: | 60 public: |
271 IBusDaemonControllerDaemonlessImpl() | 61 IBusDaemonControllerDaemonlessImpl() |
272 : is_started_(false) {} | 62 : is_started_(false) {} |
273 virtual ~IBusDaemonControllerDaemonlessImpl() {} | 63 virtual ~IBusDaemonControllerDaemonlessImpl() {} |
274 | 64 |
275 // IBusDaemonController overrides: | 65 // IBusDaemonController overrides: |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 delete g_ibus_daemon_controller; | 133 delete g_ibus_daemon_controller; |
344 g_ibus_daemon_controller = NULL; | 134 g_ibus_daemon_controller = NULL; |
345 } | 135 } |
346 | 136 |
347 // static | 137 // static |
348 IBusDaemonController* IBusDaemonController::GetInstance() { | 138 IBusDaemonController* IBusDaemonController::GetInstance() { |
349 return g_ibus_daemon_controller; | 139 return g_ibus_daemon_controller; |
350 } | 140 } |
351 | 141 |
352 } // namespace chromeos | 142 } // namespace chromeos |
OLD | NEW |