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.
17 #include <android/log.h>
19 #include <sysutils/NetlinkEvent.h>
21 #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 LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkSwitch" , ## args)
36 #define SWITCH_HEADSET_DEVPATH "/devices/virtual/switch/h2w"
37 #define SWITCH_USB_DEVPATH_GB "/devices/virtual/switch/usb_configuration"
38 #define SWITCH_USB_DEVPATH_ICS "/devices/virtual/android_usb/android0"
43 * The uevent for a usb on GB insertion looks like:
45 * change@/devices/virtual/switch/usb_configuration
47 * DEVPATH=/devices/virtual/switch/usb_configuration
49 * SWITCH_NAME=usb_configuration
56 NS_INLINE_DECL_REFCOUNTING(SwitchHandler
)
58 SwitchHandler(const char* aDevPath
, SwitchDevice aDevice
)
60 mState(SWITCH_STATE_UNKNOWN
),
66 virtual ~SwitchHandler()
70 bool CheckEvent(NetlinkEvent
* aEvent
)
72 if (strcmp(GetSubsystem(), aEvent
->getSubsystem()) ||
73 strcmp(mDevPath
, aEvent
->findParam("DEVPATH"))) {
77 mState
= ConvertState(GetStateString(aEvent
));
78 return mState
!= SWITCH_STATE_UNKNOWN
;
81 SwitchState
GetState()
86 SwitchDevice
GetType()
91 virtual const char* GetSubsystem()
96 virtual const char* GetStateString(NetlinkEvent
* aEvent
)
98 return aEvent
->findParam("SWITCH_STATE");
101 void GetInitialState()
103 nsPrintfCString
statePath("/sys%s/state", mDevPath
);
104 int fd
= open(statePath
.get(), O_RDONLY
);
109 ScopedClose
autoClose(fd
);
111 ssize_t bytesRead
= read(fd
, state
, sizeof(state
));
113 LOG("Read data from %s fails", statePath
.get());
117 if (state
[bytesRead
- 1] == '\n') {
121 state
[bytesRead
] = '\0';
122 mState
= ConvertState(state
);
125 virtual SwitchState
ConvertState(const char* aState
)
128 return aState
[0] == '0' ? SWITCH_STATE_OFF
: SWITCH_STATE_ON
;
131 const char* mDevPath
;
133 SwitchDevice mDevice
;
137 * The uevent delivered for the USB configuration under ICS looks like,
139 * change@/devices/virtual/android_usb/android0
141 * DEVPATH=/devices/virtual/android_usb/android0
142 * SUBSYSTEM=android_usb
143 * USB_STATE=CONFIGURED
146 class SwitchHandlerUsbIcs
: public SwitchHandler
149 SwitchHandlerUsbIcs(const char* aDevPath
) : SwitchHandler(aDevPath
, SWITCH_USB
)
151 SwitchHandler::GetInitialState();
154 virtual ~SwitchHandlerUsbIcs() { }
157 virtual const char* GetSubsystem()
159 return "android_usb";
162 virtual const char* GetStateString(NetlinkEvent
* aEvent
)
164 return aEvent
->findParam("USB_STATE");
167 SwitchState
ConvertState(const char* aState
)
170 return strcmp(aState
, "CONFIGURED") == 0 ? SWITCH_STATE_ON
: SWITCH_STATE_OFF
;
175 * The uevent delivered for the headset under ICS looks like,
177 * change@/devices/virtual/switch/h2w
179 * DEVPATH=/devices/virtual/switch/h2w
182 * SWITCH_STATE=2 // Headset with no mic
184 * On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug.
185 * change@/devices/virtual/switch/h2w
187 * DEVPATH=/devices/virtual/switch/h2w
189 * SWITCH_NAME=Headset
190 * SWITCH_STATE=1 // Headset with mic
193 class SwitchHandlerHeadphone
: public SwitchHandler
196 SwitchHandlerHeadphone(const char* aDevPath
) :
197 SwitchHandler(aDevPath
, SWITCH_HEADPHONES
)
199 SwitchHandler::GetInitialState();
202 virtual ~SwitchHandlerHeadphone() { }
205 SwitchState
ConvertState(const char* aState
)
209 return aState
[0] == '0' ? SWITCH_STATE_OFF
:
210 (aState
[0] == '1' ? SWITCH_STATE_HEADSET
: SWITCH_STATE_HEADPHONE
);
215 typedef nsTArray
<RefPtr
<SwitchHandler
> > SwitchHandlerArray
;
217 class SwitchEventRunnable
: public nsRunnable
220 SwitchEventRunnable(SwitchEvent
& aEvent
) : mEvent(aEvent
)
226 NotifySwitchChange(mEvent
);
233 class SwitchEventObserver MOZ_FINAL
: public IUeventObserver
235 ~SwitchEventObserver()
241 NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver
)
242 SwitchEventObserver()
244 mHeadphonesFromInputDev(false)
254 void EnableSwitch(SwitchDevice aDevice
)
256 mEventInfo
[aDevice
].mEnabled
= true;
260 void DisableSwitch(SwitchDevice aDevice
)
262 mEventInfo
[aDevice
].mEnabled
= false;
266 void Notify(const NetlinkEvent
& aEvent
)
268 SwitchState currState
;
270 SwitchDevice device
= GetEventInfo(aEvent
, currState
);
271 if (device
== SWITCH_DEVICE_UNKNOWN
) {
275 EventInfo
& info
= mEventInfo
[device
];
276 if (currState
== info
.mEvent
.status()) {
280 info
.mEvent
.status() = currState
;
283 NS_DispatchToMainThread(new SwitchEventRunnable(info
.mEvent
));
287 void Notify(SwitchDevice aDevice
, SwitchState aState
)
289 EventInfo
& info
= mEventInfo
[aDevice
];
290 if (aState
== info
.mEvent
.status()) {
294 info
.mEvent
.status() = aState
;
297 NS_DispatchToMainThread(new SwitchEventRunnable(info
.mEvent
));
301 SwitchState
GetCurrentInformation(SwitchDevice aDevice
)
303 return mEventInfo
[aDevice
].mEvent
.status();
306 void NotifyAnEvent(SwitchDevice aDevice
)
308 EventInfo
& info
= mEventInfo
[aDevice
];
309 if (info
.mEvent
.status() != SWITCH_STATE_UNKNOWN
) {
310 NS_DispatchToMainThread(new SwitchEventRunnable(info
.mEvent
));
314 bool GetHeadphonesFromInputDev()
316 return mHeadphonesFromInputDev
;
323 EventInfo() : mEnabled(false)
325 mEvent
.status() = SWITCH_STATE_UNKNOWN
;
326 mEvent
.device() = SWITCH_DEVICE_UNKNOWN
;
332 EventInfo mEventInfo
[NUM_SWITCH_DEVICE
];
334 SwitchHandlerArray mHandler
;
335 bool mHeadphonesFromInputDev
;
337 // This function might also get called on the main thread
338 // (from IsHeadphoneEventFromInputDev)
341 RefPtr
<SwitchHandlerHeadphone
> switchHeadPhone
=
342 new SwitchHandlerHeadphone(SWITCH_HEADSET_DEVPATH
);
344 // If the initial state is unknown, it means the headphone event is from input dev
345 mHeadphonesFromInputDev
= switchHeadPhone
->GetState() == SWITCH_STATE_UNKNOWN
? true : false;
347 if (!mHeadphonesFromInputDev
) {
348 mHandler
.AppendElement(switchHeadPhone
);
350 // If headphone status will be notified from input dev then initialize
351 // status to "off" and wait for event notification.
352 mEventInfo
[SWITCH_HEADPHONES
].mEvent
.device() = SWITCH_HEADPHONES
;
353 mEventInfo
[SWITCH_HEADPHONES
].mEvent
.status() = SWITCH_STATE_OFF
;
355 mHandler
.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB
, SWITCH_USB
));
356 mHandler
.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS
));
358 SwitchHandlerArray::index_type handlerIndex
;
359 SwitchHandlerArray::size_type numHandlers
= mHandler
.Length();
361 for (handlerIndex
= 0; handlerIndex
< numHandlers
; handlerIndex
++) {
362 SwitchState state
= mHandler
[handlerIndex
]->GetState();
363 if (state
== SWITCH_STATE_UNKNOWN
) {
367 SwitchDevice device
= mHandler
[handlerIndex
]->GetType();
368 mEventInfo
[device
].mEvent
.device() = device
;
369 mEventInfo
[device
].mEvent
.status() = state
;
373 SwitchDevice
GetEventInfo(const NetlinkEvent
& aEvent
, SwitchState
& aState
)
375 //working around the android code not being const-correct
376 NetlinkEvent
*e
= const_cast<NetlinkEvent
*>(&aEvent
);
378 for (size_t i
= 0; i
< mHandler
.Length(); i
++) {
379 if (mHandler
[i
]->CheckEvent(e
)) {
380 aState
= mHandler
[i
]->GetState();
381 return mHandler
[i
]->GetType();
384 return SWITCH_DEVICE_UNKNOWN
;
388 static RefPtr
<SwitchEventObserver
> sSwitchObserver
;
391 InitializeResourceIfNeed()
393 if (!sSwitchObserver
) {
394 sSwitchObserver
= new SwitchEventObserver();
395 RegisterUeventListener(sSwitchObserver
);
400 ReleaseResourceIfNeed()
402 if (sSwitchObserver
->GetEnableCount() == 0) {
403 UnregisterUeventListener(sSwitchObserver
);
404 sSwitchObserver
= nullptr;
409 EnableSwitchNotificationsIOThread(SwitchDevice aDevice
, Monitor
*aMonitor
)
411 InitializeResourceIfNeed();
412 sSwitchObserver
->EnableSwitch(aDevice
);
414 MonitorAutoLock
lock(*aMonitor
);
418 // Notify the latest state if IO thread has the information.
419 if (sSwitchObserver
->GetEnableCount() > 1) {
420 sSwitchObserver
->NotifyAnEvent(aDevice
);
425 EnableSwitchNotifications(SwitchDevice aDevice
)
427 Monitor
monitor("EnableSwitch.monitor");
429 MonitorAutoLock
lock(monitor
);
430 XRE_GetIOMessageLoop()->PostTask(
432 NewRunnableFunction(EnableSwitchNotificationsIOThread
, aDevice
, &monitor
));
438 DisableSwitchNotificationsIOThread(SwitchDevice aDevice
)
440 MOZ_ASSERT(sSwitchObserver
->GetEnableCount());
441 sSwitchObserver
->DisableSwitch(aDevice
);
442 ReleaseResourceIfNeed();
446 DisableSwitchNotifications(SwitchDevice aDevice
)
448 XRE_GetIOMessageLoop()->PostTask(
450 NewRunnableFunction(DisableSwitchNotificationsIOThread
, aDevice
));
454 GetCurrentSwitchState(SwitchDevice aDevice
)
456 MOZ_ASSERT(sSwitchObserver
&& sSwitchObserver
->GetEnableCount());
457 return sSwitchObserver
->GetCurrentInformation(aDevice
);
461 NotifySwitchStateIOThread(SwitchDevice aDevice
, SwitchState aState
)
463 InitializeResourceIfNeed();
464 sSwitchObserver
->Notify(aDevice
, aState
);
467 void NotifySwitchStateFromInputDevice(SwitchDevice aDevice
, SwitchState aState
)
469 XRE_GetIOMessageLoop()->PostTask(
471 NewRunnableFunction(NotifySwitchStateIOThread
, aDevice
, aState
));
474 bool IsHeadphoneEventFromInputDev()
476 // Instead of calling InitializeResourceIfNeed, create new SwitchEventObserver
477 // to prevent calling RegisterUeventListener in main thread.
478 RefPtr
<SwitchEventObserver
> switchObserver
= new SwitchEventObserver();
479 return switchObserver
->GetHeadphonesFromInputDev();