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
53 class SwitchHandler
: public RefCounted
<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 LOG("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
: public IUeventObserver
,
232 public RefCounted
<SwitchEventObserver
>
235 SwitchEventObserver() : mEnableCount(0)
240 ~SwitchEventObserver()
250 void EnableSwitch(SwitchDevice aDevice
)
252 mEventInfo
[aDevice
].mEnabled
= true;
256 void DisableSwitch(SwitchDevice aDevice
)
258 mEventInfo
[aDevice
].mEnabled
= false;
262 void Notify(const NetlinkEvent
& aEvent
)
264 SwitchState currState
;
266 SwitchDevice device
= GetEventInfo(aEvent
, currState
);
267 if (device
== SWITCH_DEVICE_UNKNOWN
) {
271 EventInfo
& info
= mEventInfo
[device
];
272 if (currState
== info
.mEvent
.status()) {
276 info
.mEvent
.status() = currState
;
279 NS_DispatchToMainThread(new SwitchEventRunnable(info
.mEvent
));
283 SwitchState
GetCurrentInformation(SwitchDevice aDevice
)
285 return mEventInfo
[aDevice
].mEvent
.status();
288 void NotifyAnEvent(SwitchDevice aDevice
)
290 EventInfo
& info
= mEventInfo
[aDevice
];
291 if (info
.mEvent
.status() != SWITCH_STATE_UNKNOWN
) {
292 NS_DispatchToMainThread(new SwitchEventRunnable(info
.mEvent
));
299 EventInfo() : mEnabled(false)
301 mEvent
.status() = SWITCH_STATE_UNKNOWN
;
302 mEvent
.device() = SWITCH_DEVICE_UNKNOWN
;
308 EventInfo mEventInfo
[NUM_SWITCH_DEVICE
];
310 SwitchHandlerArray mHandler
;
314 mHandler
.AppendElement(new SwitchHandlerHeadphone(SWITCH_HEADSET_DEVPATH
));
315 mHandler
.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB
, SWITCH_USB
));
316 mHandler
.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS
));
318 SwitchHandlerArray::index_type handlerIndex
;
319 SwitchHandlerArray::size_type numHandlers
= mHandler
.Length();
321 for (handlerIndex
= 0; handlerIndex
< numHandlers
; handlerIndex
++) {
322 SwitchState state
= mHandler
[handlerIndex
]->GetState();
323 if (state
== SWITCH_STATE_UNKNOWN
) {
327 SwitchDevice device
= mHandler
[handlerIndex
]->GetType();
328 mEventInfo
[device
].mEvent
.device() = device
;
329 mEventInfo
[device
].mEvent
.status() = state
;
333 SwitchDevice
GetEventInfo(const NetlinkEvent
& aEvent
, SwitchState
& aState
)
335 //working around the android code not being const-correct
336 NetlinkEvent
*e
= const_cast<NetlinkEvent
*>(&aEvent
);
338 for (size_t i
= 0; i
< mHandler
.Length(); i
++) {
339 if (mHandler
[i
]->CheckEvent(e
)) {
340 aState
= mHandler
[i
]->GetState();
341 return mHandler
[i
]->GetType();
344 return SWITCH_DEVICE_UNKNOWN
;
348 static RefPtr
<SwitchEventObserver
> sSwitchObserver
;
351 InitializeResourceIfNeed()
353 if (!sSwitchObserver
) {
354 sSwitchObserver
= new SwitchEventObserver();
355 RegisterUeventListener(sSwitchObserver
);
360 ReleaseResourceIfNeed()
362 if (sSwitchObserver
->GetEnableCount() == 0) {
363 UnregisterUeventListener(sSwitchObserver
);
364 sSwitchObserver
= nullptr;
369 EnableSwitchNotificationsIOThread(SwitchDevice aDevice
, Monitor
*aMonitor
)
371 InitializeResourceIfNeed();
372 sSwitchObserver
->EnableSwitch(aDevice
);
374 MonitorAutoLock
lock(*aMonitor
);
378 // Notify the latest state if IO thread has the information.
379 if (sSwitchObserver
->GetEnableCount() > 1) {
380 sSwitchObserver
->NotifyAnEvent(aDevice
);
385 EnableSwitchNotifications(SwitchDevice aDevice
)
387 Monitor
monitor("EnableSwitch.monitor");
389 MonitorAutoLock
lock(monitor
);
390 XRE_GetIOMessageLoop()->PostTask(
392 NewRunnableFunction(EnableSwitchNotificationsIOThread
, aDevice
, &monitor
));
398 DisableSwitchNotificationsIOThread(SwitchDevice aDevice
)
400 MOZ_ASSERT(sSwitchObserver
->GetEnableCount());
401 sSwitchObserver
->DisableSwitch(aDevice
);
402 ReleaseResourceIfNeed();
406 DisableSwitchNotifications(SwitchDevice aDevice
)
408 XRE_GetIOMessageLoop()->PostTask(
410 NewRunnableFunction(DisableSwitchNotificationsIOThread
, aDevice
));
414 GetCurrentSwitchState(SwitchDevice aDevice
)
416 MOZ_ASSERT(sSwitchObserver
&& sSwitchObserver
->GetEnableCount());
417 return sSwitchObserver
->GetCurrentInformation(aDevice
);