1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <sysutils/NetlinkEvent.h>
20 #include "base/message_loop.h"
24 #include "mozilla/FileUtils.h"
25 #include "mozilla/RefPtr.h"
26 #include "mozilla/Monitor.h"
27 #include "nsPrintfCString.h"
28 #include "nsXULAppAPI.h"
29 #include "nsThreadUtils.h"
30 #include "UeventPoller.h"
32 using namespace mozilla::hal
;
34 #define SWITCH_HEADSET_DEVPATH "/devices/virtual/switch/h2w"
35 #define SWITCH_USB_DEVPATH_GB "/devices/virtual/switch/usb_configuration"
36 #define SWITCH_USB_DEVPATH_ICS "/devices/virtual/android_usb/android0"
41 * The uevent for a usb on GB insertion looks like:
43 * change@/devices/virtual/switch/usb_configuration
45 * DEVPATH=/devices/virtual/switch/usb_configuration
47 * SWITCH_NAME=usb_configuration
54 NS_INLINE_DECL_REFCOUNTING(SwitchHandler
)
56 SwitchHandler(const char* aDevPath
, SwitchDevice aDevice
)
58 mState(SWITCH_STATE_UNKNOWN
),
64 virtual ~SwitchHandler()
68 bool CheckEvent(NetlinkEvent
* aEvent
)
70 if (strcmp(GetSubsystem(), aEvent
->getSubsystem()) ||
71 strcmp(mDevPath
, aEvent
->findParam("DEVPATH"))) {
75 mState
= ConvertState(GetStateString(aEvent
));
76 return mState
!= SWITCH_STATE_UNKNOWN
;
79 SwitchState
GetState()
84 SwitchDevice
GetType()
89 virtual const char* GetSubsystem()
94 virtual const char* GetStateString(NetlinkEvent
* aEvent
)
96 return aEvent
->findParam("SWITCH_STATE");
99 void GetInitialState()
101 nsPrintfCString
statePath("/sys%s/state", mDevPath
);
102 int fd
= open(statePath
.get(), O_RDONLY
);
107 ScopedClose
autoClose(fd
);
109 ssize_t bytesRead
= read(fd
, state
, sizeof(state
));
111 HAL_ERR("Read data from %s fails", statePath
.get());
115 if (state
[bytesRead
- 1] == '\n') {
119 state
[bytesRead
] = '\0';
120 mState
= ConvertState(state
);
123 virtual SwitchState
ConvertState(const char* aState
)
126 return aState
[0] == '0' ? SWITCH_STATE_OFF
: SWITCH_STATE_ON
;
129 const char* mDevPath
;
131 SwitchDevice mDevice
;
135 * The uevent delivered for the USB configuration under ICS looks like,
137 * change@/devices/virtual/android_usb/android0
139 * DEVPATH=/devices/virtual/android_usb/android0
140 * SUBSYSTEM=android_usb
141 * USB_STATE=CONFIGURED
144 class SwitchHandlerUsbIcs
: public SwitchHandler
147 SwitchHandlerUsbIcs(const char* aDevPath
) : SwitchHandler(aDevPath
, SWITCH_USB
)
149 SwitchHandler::GetInitialState();
152 virtual ~SwitchHandlerUsbIcs() { }
155 virtual const char* GetSubsystem()
157 return "android_usb";
160 virtual const char* GetStateString(NetlinkEvent
* aEvent
)
162 return aEvent
->findParam("USB_STATE");
165 SwitchState
ConvertState(const char* aState
)
168 return strcmp(aState
, "CONFIGURED") == 0 ? SWITCH_STATE_ON
: SWITCH_STATE_OFF
;
173 * The uevent delivered for the headset under ICS looks like,
175 * change@/devices/virtual/switch/h2w
177 * DEVPATH=/devices/virtual/switch/h2w
180 * SWITCH_STATE=2 // Headset with no mic
182 * On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug.
183 * change@/devices/virtual/switch/h2w
185 * DEVPATH=/devices/virtual/switch/h2w
187 * SWITCH_NAME=Headset
188 * SWITCH_STATE=1 // Headset with mic
191 class SwitchHandlerHeadphone
: public SwitchHandler
194 SwitchHandlerHeadphone(const char* aDevPath
) :
195 SwitchHandler(aDevPath
, SWITCH_HEADPHONES
)
197 SwitchHandler::GetInitialState();
200 virtual ~SwitchHandlerHeadphone() { }
203 SwitchState
ConvertState(const char* aState
)
207 return aState
[0] == '0' ? SWITCH_STATE_OFF
:
208 (aState
[0] == '1' ? SWITCH_STATE_HEADSET
: SWITCH_STATE_HEADPHONE
);
213 typedef nsTArray
<RefPtr
<SwitchHandler
> > SwitchHandlerArray
;
215 class SwitchEventRunnable
: public nsRunnable
218 SwitchEventRunnable(SwitchEvent
& aEvent
) : mEvent(aEvent
)
224 NotifySwitchChange(mEvent
);
231 class SwitchEventObserver MOZ_FINAL
: public IUeventObserver
233 ~SwitchEventObserver()
239 NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver
)
240 SwitchEventObserver()
242 mHeadphonesFromInputDev(false)
252 void EnableSwitch(SwitchDevice aDevice
)
254 mEventInfo
[aDevice
].mEnabled
= true;
258 void DisableSwitch(SwitchDevice aDevice
)
260 mEventInfo
[aDevice
].mEnabled
= false;
264 void Notify(const NetlinkEvent
& aEvent
)
266 SwitchState currState
;
268 SwitchDevice device
= GetEventInfo(aEvent
, currState
);
269 if (device
== SWITCH_DEVICE_UNKNOWN
) {
273 EventInfo
& info
= mEventInfo
[device
];
274 if (currState
== info
.mEvent
.status()) {
278 info
.mEvent
.status() = currState
;
281 NS_DispatchToMainThread(new SwitchEventRunnable(info
.mEvent
));
285 void Notify(SwitchDevice aDevice
, SwitchState aState
)
287 EventInfo
& info
= mEventInfo
[aDevice
];
288 if (aState
== info
.mEvent
.status()) {
292 info
.mEvent
.status() = aState
;
295 NS_DispatchToMainThread(new SwitchEventRunnable(info
.mEvent
));
299 SwitchState
GetCurrentInformation(SwitchDevice aDevice
)
301 return mEventInfo
[aDevice
].mEvent
.status();
304 void NotifyAnEvent(SwitchDevice aDevice
)
306 EventInfo
& info
= mEventInfo
[aDevice
];
307 if (info
.mEvent
.status() != SWITCH_STATE_UNKNOWN
) {
308 NS_DispatchToMainThread(new SwitchEventRunnable(info
.mEvent
));
312 bool GetHeadphonesFromInputDev()
314 return mHeadphonesFromInputDev
;
321 EventInfo() : mEnabled(false)
323 mEvent
.status() = SWITCH_STATE_UNKNOWN
;
324 mEvent
.device() = SWITCH_DEVICE_UNKNOWN
;
330 EventInfo mEventInfo
[NUM_SWITCH_DEVICE
];
332 SwitchHandlerArray mHandler
;
333 bool mHeadphonesFromInputDev
;
335 // This function might also get called on the main thread
336 // (from IsHeadphoneEventFromInputDev)
339 RefPtr
<SwitchHandlerHeadphone
> switchHeadPhone
=
340 new SwitchHandlerHeadphone(SWITCH_HEADSET_DEVPATH
);
342 // If the initial state is unknown, it means the headphone event is from input dev
343 mHeadphonesFromInputDev
= switchHeadPhone
->GetState() == SWITCH_STATE_UNKNOWN
? true : false;
345 if (!mHeadphonesFromInputDev
) {
346 mHandler
.AppendElement(switchHeadPhone
);
348 // If headphone status will be notified from input dev then initialize
349 // status to "off" and wait for event notification.
350 mEventInfo
[SWITCH_HEADPHONES
].mEvent
.device() = SWITCH_HEADPHONES
;
351 mEventInfo
[SWITCH_HEADPHONES
].mEvent
.status() = SWITCH_STATE_OFF
;
353 mHandler
.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB
, SWITCH_USB
));
354 mHandler
.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS
));
356 SwitchHandlerArray::index_type handlerIndex
;
357 SwitchHandlerArray::size_type numHandlers
= mHandler
.Length();
359 for (handlerIndex
= 0; handlerIndex
< numHandlers
; handlerIndex
++) {
360 SwitchState state
= mHandler
[handlerIndex
]->GetState();
361 if (state
== SWITCH_STATE_UNKNOWN
) {
365 SwitchDevice device
= mHandler
[handlerIndex
]->GetType();
366 mEventInfo
[device
].mEvent
.device() = device
;
367 mEventInfo
[device
].mEvent
.status() = state
;
371 SwitchDevice
GetEventInfo(const NetlinkEvent
& aEvent
, SwitchState
& aState
)
373 //working around the android code not being const-correct
374 NetlinkEvent
*e
= const_cast<NetlinkEvent
*>(&aEvent
);
376 for (size_t i
= 0; i
< mHandler
.Length(); i
++) {
377 if (mHandler
[i
]->CheckEvent(e
)) {
378 aState
= mHandler
[i
]->GetState();
379 return mHandler
[i
]->GetType();
382 return SWITCH_DEVICE_UNKNOWN
;
386 static RefPtr
<SwitchEventObserver
> sSwitchObserver
;
389 InitializeResourceIfNeed()
391 if (!sSwitchObserver
) {
392 sSwitchObserver
= new SwitchEventObserver();
393 RegisterUeventListener(sSwitchObserver
);
398 ReleaseResourceIfNeed()
400 if (sSwitchObserver
->GetEnableCount() == 0) {
401 UnregisterUeventListener(sSwitchObserver
);
402 sSwitchObserver
= nullptr;
407 EnableSwitchNotificationsIOThread(SwitchDevice aDevice
, Monitor
*aMonitor
)
409 InitializeResourceIfNeed();
410 sSwitchObserver
->EnableSwitch(aDevice
);
412 MonitorAutoLock
lock(*aMonitor
);
416 // Notify the latest state if IO thread has the information.
417 if (sSwitchObserver
->GetEnableCount() > 1) {
418 sSwitchObserver
->NotifyAnEvent(aDevice
);
423 EnableSwitchNotifications(SwitchDevice aDevice
)
425 Monitor
monitor("EnableSwitch.monitor");
427 MonitorAutoLock
lock(monitor
);
428 XRE_GetIOMessageLoop()->PostTask(
430 NewRunnableFunction(EnableSwitchNotificationsIOThread
, aDevice
, &monitor
));
436 DisableSwitchNotificationsIOThread(SwitchDevice aDevice
)
438 MOZ_ASSERT(sSwitchObserver
->GetEnableCount());
439 sSwitchObserver
->DisableSwitch(aDevice
);
440 ReleaseResourceIfNeed();
444 DisableSwitchNotifications(SwitchDevice aDevice
)
446 XRE_GetIOMessageLoop()->PostTask(
448 NewRunnableFunction(DisableSwitchNotificationsIOThread
, aDevice
));
452 GetCurrentSwitchState(SwitchDevice aDevice
)
454 MOZ_ASSERT(sSwitchObserver
&& sSwitchObserver
->GetEnableCount());
455 return sSwitchObserver
->GetCurrentInformation(aDevice
);
459 NotifySwitchStateIOThread(SwitchDevice aDevice
, SwitchState aState
)
461 InitializeResourceIfNeed();
462 sSwitchObserver
->Notify(aDevice
, aState
);
465 void NotifySwitchStateFromInputDevice(SwitchDevice aDevice
, SwitchState aState
)
467 XRE_GetIOMessageLoop()->PostTask(
469 NewRunnableFunction(NotifySwitchStateIOThread
, aDevice
, aState
));
472 bool IsHeadphoneEventFromInputDev()
474 // Instead of calling InitializeResourceIfNeed, create new SwitchEventObserver
475 // to prevent calling RegisterUeventListener in main thread.
476 RefPtr
<SwitchEventObserver
> switchObserver
= new SwitchEventObserver();
477 return switchObserver
->GetHeadphonesFromInputDev();