hal/gonk/GonkSwitch.cpp

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:2cfb839e4da8
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android/log.h>
18 #include <fcntl.h>
19 #include <sysutils/NetlinkEvent.h>
20 #include <cutils/properties.h>
21
22 #include "base/message_loop.h"
23
24 #include "Hal.h"
25 #include "mozilla/FileUtils.h"
26 #include "mozilla/RefPtr.h"
27 #include "mozilla/Monitor.h"
28 #include "nsPrintfCString.h"
29 #include "nsXULAppAPI.h"
30 #include "nsThreadUtils.h"
31 #include "UeventPoller.h"
32
33 using namespace mozilla::hal;
34
35 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkSwitch" , ## args)
36
37 #define SWITCH_HEADSET_DEVPATH "/devices/virtual/switch/h2w"
38 #define SWITCH_USB_DEVPATH_GB "/devices/virtual/switch/usb_configuration"
39 #define SWITCH_USB_DEVPATH_ICS "/devices/virtual/android_usb/android0"
40
41 namespace mozilla {
42 namespace hal_impl {
43 /**
44 * The uevent for a usb on GB insertion looks like:
45 *
46 * change@/devices/virtual/switch/usb_configuration
47 * ACTION=change
48 * DEVPATH=/devices/virtual/switch/usb_configuration
49 * SUBSYSTEM=switch
50 * SWITCH_NAME=usb_configuration
51 * SWITCH_STATE=0
52 * SEQNUM=5038
53 */
54 class SwitchHandler
55 {
56 public:
57 NS_INLINE_DECL_REFCOUNTING(SwitchHandler)
58
59 SwitchHandler(const char* aDevPath, SwitchDevice aDevice)
60 : mDevPath(aDevPath),
61 mState(SWITCH_STATE_UNKNOWN),
62 mDevice(aDevice)
63 {
64 GetInitialState();
65 }
66
67 virtual ~SwitchHandler()
68 {
69 }
70
71 bool CheckEvent(NetlinkEvent* aEvent)
72 {
73 if (strcmp(GetSubsystem(), aEvent->getSubsystem()) ||
74 strcmp(mDevPath, aEvent->findParam("DEVPATH"))) {
75 return false;
76 }
77
78 mState = ConvertState(GetStateString(aEvent));
79 return mState != SWITCH_STATE_UNKNOWN;
80 }
81
82 SwitchState GetState()
83 {
84 return mState;
85 }
86
87 SwitchDevice GetType()
88 {
89 return mDevice;
90 }
91 protected:
92 virtual const char* GetSubsystem()
93 {
94 return "switch";
95 }
96
97 virtual const char* GetStateString(NetlinkEvent* aEvent)
98 {
99 return aEvent->findParam("SWITCH_STATE");
100 }
101
102 void GetInitialState()
103 {
104 nsPrintfCString statePath("/sys%s/state", mDevPath);
105 int fd = open(statePath.get(), O_RDONLY);
106 if (fd <= 0) {
107 return;
108 }
109
110 ScopedClose autoClose(fd);
111 char state[16];
112 ssize_t bytesRead = read(fd, state, sizeof(state));
113 if (bytesRead < 0) {
114 LOG("Read data from %s fails", statePath.get());
115 return;
116 }
117
118 if (state[bytesRead - 1] == '\n') {
119 bytesRead--;
120 }
121
122 state[bytesRead] = '\0';
123 mState = ConvertState(state);
124 }
125
126 virtual SwitchState ConvertState(const char* aState)
127 {
128 MOZ_ASSERT(aState);
129 return aState[0] == '0' ? SWITCH_STATE_OFF : SWITCH_STATE_ON;
130 }
131
132 const char* mDevPath;
133 SwitchState mState;
134 SwitchDevice mDevice;
135 };
136
137 /**
138 * The uevent delivered for the USB configuration under ICS looks like,
139 *
140 * change@/devices/virtual/android_usb/android0
141 * ACTION=change
142 * DEVPATH=/devices/virtual/android_usb/android0
143 * SUBSYSTEM=android_usb
144 * USB_STATE=CONFIGURED
145 * SEQNUM=1802
146 */
147 class SwitchHandlerUsbIcs: public SwitchHandler
148 {
149 public:
150 SwitchHandlerUsbIcs(const char* aDevPath) : SwitchHandler(aDevPath, SWITCH_USB)
151 {
152 SwitchHandler::GetInitialState();
153 }
154
155 virtual ~SwitchHandlerUsbIcs() { }
156
157 protected:
158 virtual const char* GetSubsystem()
159 {
160 return "android_usb";
161 }
162
163 virtual const char* GetStateString(NetlinkEvent* aEvent)
164 {
165 return aEvent->findParam("USB_STATE");
166 }
167
168 SwitchState ConvertState(const char* aState)
169 {
170 MOZ_ASSERT(aState);
171 return strcmp(aState, "CONFIGURED") == 0 ? SWITCH_STATE_ON : SWITCH_STATE_OFF;
172 }
173 };
174
175 /**
176 * The uevent delivered for the headset under ICS looks like,
177 *
178 * change@/devices/virtual/switch/h2w
179 * ACTION=change
180 * DEVPATH=/devices/virtual/switch/h2w
181 * SUBSYSTEM=switch
182 * SWITCH_NAME=h2w
183 * SWITCH_STATE=2 // Headset with no mic
184 * SEQNUM=2581
185 * On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug.
186 * change@/devices/virtual/switch/h2w
187 * ACTION=change
188 * DEVPATH=/devices/virtual/switch/h2w
189 * SUBSYSTEM=switch
190 * SWITCH_NAME=Headset
191 * SWITCH_STATE=1 // Headset with mic
192 * SEQNUM=1602
193 */
194 class SwitchHandlerHeadphone: public SwitchHandler
195 {
196 public:
197 SwitchHandlerHeadphone(const char* aDevPath) :
198 SwitchHandler(aDevPath, SWITCH_HEADPHONES)
199 {
200 SwitchHandler::GetInitialState();
201 }
202
203 virtual ~SwitchHandlerHeadphone() { }
204
205 protected:
206 SwitchState ConvertState(const char* aState)
207 {
208 MOZ_ASSERT(aState);
209
210 return aState[0] == '0' ? SWITCH_STATE_OFF :
211 (aState[0] == '1' ? SWITCH_STATE_HEADSET : SWITCH_STATE_HEADPHONE);
212 }
213 };
214
215
216 typedef nsTArray<RefPtr<SwitchHandler> > SwitchHandlerArray;
217
218 class SwitchEventRunnable : public nsRunnable
219 {
220 public:
221 SwitchEventRunnable(SwitchEvent& aEvent) : mEvent(aEvent)
222 {
223 }
224
225 NS_IMETHOD Run()
226 {
227 NotifySwitchChange(mEvent);
228 return NS_OK;
229 }
230 private:
231 SwitchEvent mEvent;
232 };
233
234 class SwitchEventObserver MOZ_FINAL : public IUeventObserver
235 {
236 ~SwitchEventObserver()
237 {
238 mHandler.Clear();
239 }
240
241 public:
242 NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver)
243 SwitchEventObserver() : mEnableCount(0)
244 {
245 Init();
246 }
247
248 int GetEnableCount()
249 {
250 return mEnableCount;
251 }
252
253 void EnableSwitch(SwitchDevice aDevice)
254 {
255 mEventInfo[aDevice].mEnabled = true;
256 mEnableCount++;
257 }
258
259 void DisableSwitch(SwitchDevice aDevice)
260 {
261 mEventInfo[aDevice].mEnabled = false;
262 mEnableCount--;
263 }
264
265 void Notify(const NetlinkEvent& aEvent)
266 {
267 SwitchState currState;
268
269 SwitchDevice device = GetEventInfo(aEvent, currState);
270 if (device == SWITCH_DEVICE_UNKNOWN) {
271 return;
272 }
273
274 EventInfo& info = mEventInfo[device];
275 if (currState == info.mEvent.status()) {
276 return;
277 }
278
279 info.mEvent.status() = currState;
280
281 if (info.mEnabled) {
282 NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
283 }
284 }
285
286 void Notify(SwitchDevice aDevice, SwitchState aState)
287 {
288 EventInfo& info = mEventInfo[aDevice];
289 if (aState == info.mEvent.status()) {
290 return;
291 }
292
293 info.mEvent.status() = aState;
294
295 if (info.mEnabled) {
296 NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
297 }
298 }
299
300 SwitchState GetCurrentInformation(SwitchDevice aDevice)
301 {
302 return mEventInfo[aDevice].mEvent.status();
303 }
304
305 void NotifyAnEvent(SwitchDevice aDevice)
306 {
307 EventInfo& info = mEventInfo[aDevice];
308 if (info.mEvent.status() != SWITCH_STATE_UNKNOWN) {
309 NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
310 }
311 }
312 private:
313 class EventInfo
314 {
315 public:
316 EventInfo() : mEnabled(false)
317 {
318 mEvent.status() = SWITCH_STATE_UNKNOWN;
319 mEvent.device() = SWITCH_DEVICE_UNKNOWN;
320 }
321 SwitchEvent mEvent;
322 bool mEnabled;
323 };
324
325 EventInfo mEventInfo[NUM_SWITCH_DEVICE];
326 size_t mEnableCount;
327 SwitchHandlerArray mHandler;
328
329 void Init()
330 {
331 char value[PROPERTY_VALUE_MAX];
332 property_get("ro.moz.devinputjack", value, "0");
333 bool headphonesFromInputDev = !strcmp(value, "1");
334
335 if (!headphonesFromInputDev) {
336 mHandler.AppendElement(new SwitchHandlerHeadphone(SWITCH_HEADSET_DEVPATH));
337 } else {
338 // If headphone status will be notified from input dev then initialize
339 // status to "off" and wait for event notification.
340 mEventInfo[SWITCH_HEADPHONES].mEvent.device() = SWITCH_HEADPHONES;
341 mEventInfo[SWITCH_HEADPHONES].mEvent.status() = SWITCH_STATE_OFF;
342 }
343 mHandler.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB, SWITCH_USB));
344 mHandler.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS));
345
346 SwitchHandlerArray::index_type handlerIndex;
347 SwitchHandlerArray::size_type numHandlers = mHandler.Length();
348
349 for (handlerIndex = 0; handlerIndex < numHandlers; handlerIndex++) {
350 SwitchState state = mHandler[handlerIndex]->GetState();
351 if (state == SWITCH_STATE_UNKNOWN) {
352 continue;
353 }
354
355 SwitchDevice device = mHandler[handlerIndex]->GetType();
356 mEventInfo[device].mEvent.device() = device;
357 mEventInfo[device].mEvent.status() = state;
358 }
359 }
360
361 SwitchDevice GetEventInfo(const NetlinkEvent& aEvent, SwitchState& aState)
362 {
363 //working around the android code not being const-correct
364 NetlinkEvent *e = const_cast<NetlinkEvent*>(&aEvent);
365
366 for (size_t i = 0; i < mHandler.Length(); i++) {
367 if (mHandler[i]->CheckEvent(e)) {
368 aState = mHandler[i]->GetState();
369 return mHandler[i]->GetType();
370 }
371 }
372 return SWITCH_DEVICE_UNKNOWN;
373 }
374 };
375
376 static RefPtr<SwitchEventObserver> sSwitchObserver;
377
378 static void
379 InitializeResourceIfNeed()
380 {
381 if (!sSwitchObserver) {
382 sSwitchObserver = new SwitchEventObserver();
383 RegisterUeventListener(sSwitchObserver);
384 }
385 }
386
387 static void
388 ReleaseResourceIfNeed()
389 {
390 if (sSwitchObserver->GetEnableCount() == 0) {
391 UnregisterUeventListener(sSwitchObserver);
392 sSwitchObserver = nullptr;
393 }
394 }
395
396 static void
397 EnableSwitchNotificationsIOThread(SwitchDevice aDevice, Monitor *aMonitor)
398 {
399 InitializeResourceIfNeed();
400 sSwitchObserver->EnableSwitch(aDevice);
401 {
402 MonitorAutoLock lock(*aMonitor);
403 lock.Notify();
404 }
405
406 // Notify the latest state if IO thread has the information.
407 if (sSwitchObserver->GetEnableCount() > 1) {
408 sSwitchObserver->NotifyAnEvent(aDevice);
409 }
410 }
411
412 void
413 EnableSwitchNotifications(SwitchDevice aDevice)
414 {
415 Monitor monitor("EnableSwitch.monitor");
416 {
417 MonitorAutoLock lock(monitor);
418 XRE_GetIOMessageLoop()->PostTask(
419 FROM_HERE,
420 NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice, &monitor));
421 lock.Wait();
422 }
423 }
424
425 static void
426 DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
427 {
428 MOZ_ASSERT(sSwitchObserver->GetEnableCount());
429 sSwitchObserver->DisableSwitch(aDevice);
430 ReleaseResourceIfNeed();
431 }
432
433 void
434 DisableSwitchNotifications(SwitchDevice aDevice)
435 {
436 XRE_GetIOMessageLoop()->PostTask(
437 FROM_HERE,
438 NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
439 }
440
441 SwitchState
442 GetCurrentSwitchState(SwitchDevice aDevice)
443 {
444 MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
445 return sSwitchObserver->GetCurrentInformation(aDevice);
446 }
447
448 static void
449 NotifySwitchStateIOThread(SwitchDevice aDevice, SwitchState aState)
450 {
451 sSwitchObserver->Notify(aDevice, aState);
452 }
453
454 void NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
455 {
456 InitializeResourceIfNeed();
457 XRE_GetIOMessageLoop()->PostTask(
458 FROM_HERE,
459 NewRunnableFunction(NotifySwitchStateIOThread, aDevice, aState));
460 }
461 } // hal_impl
462 } //mozilla

mercurial