Bumping manifests a=b2g-bump
[gecko.git] / hal / gonk / GonkSwitch.cpp
blobdfa38ae91ad2d6f92a4776d60221f25bda97c35e
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>
18 #include <fcntl.h>
19 #include <sysutils/NetlinkEvent.h>
21 #include "base/message_loop.h"
23 #include "Hal.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"
40 namespace mozilla {
41 namespace hal_impl {
42 /**
43 * The uevent for a usb on GB insertion looks like:
45 * change@/devices/virtual/switch/usb_configuration
46 * ACTION=change
47 * DEVPATH=/devices/virtual/switch/usb_configuration
48 * SUBSYSTEM=switch
49 * SWITCH_NAME=usb_configuration
50 * SWITCH_STATE=0
51 * SEQNUM=5038
53 class SwitchHandler
55 public:
56 NS_INLINE_DECL_REFCOUNTING(SwitchHandler)
58 SwitchHandler(const char* aDevPath, SwitchDevice aDevice)
59 : mDevPath(aDevPath),
60 mState(SWITCH_STATE_UNKNOWN),
61 mDevice(aDevice)
63 GetInitialState();
66 virtual ~SwitchHandler()
70 bool CheckEvent(NetlinkEvent* aEvent)
72 if (strcmp(GetSubsystem(), aEvent->getSubsystem()) ||
73 strcmp(mDevPath, aEvent->findParam("DEVPATH"))) {
74 return false;
77 mState = ConvertState(GetStateString(aEvent));
78 return mState != SWITCH_STATE_UNKNOWN;
81 SwitchState GetState()
83 return mState;
86 SwitchDevice GetType()
88 return mDevice;
90 protected:
91 virtual const char* GetSubsystem()
93 return "switch";
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);
105 if (fd <= 0) {
106 return;
109 ScopedClose autoClose(fd);
110 char state[16];
111 ssize_t bytesRead = read(fd, state, sizeof(state));
112 if (bytesRead < 0) {
113 LOG("Read data from %s fails", statePath.get());
114 return;
117 if (state[bytesRead - 1] == '\n') {
118 bytesRead--;
121 state[bytesRead] = '\0';
122 mState = ConvertState(state);
125 virtual SwitchState ConvertState(const char* aState)
127 MOZ_ASSERT(aState);
128 return aState[0] == '0' ? SWITCH_STATE_OFF : SWITCH_STATE_ON;
131 const char* mDevPath;
132 SwitchState mState;
133 SwitchDevice mDevice;
137 * The uevent delivered for the USB configuration under ICS looks like,
139 * change@/devices/virtual/android_usb/android0
140 * ACTION=change
141 * DEVPATH=/devices/virtual/android_usb/android0
142 * SUBSYSTEM=android_usb
143 * USB_STATE=CONFIGURED
144 * SEQNUM=1802
146 class SwitchHandlerUsbIcs: public SwitchHandler
148 public:
149 SwitchHandlerUsbIcs(const char* aDevPath) : SwitchHandler(aDevPath, SWITCH_USB)
151 SwitchHandler::GetInitialState();
154 virtual ~SwitchHandlerUsbIcs() { }
156 protected:
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)
169 MOZ_ASSERT(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
178 * ACTION=change
179 * DEVPATH=/devices/virtual/switch/h2w
180 * SUBSYSTEM=switch
181 * SWITCH_NAME=h2w
182 * SWITCH_STATE=2 // Headset with no mic
183 * SEQNUM=2581
184 * On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug.
185 * change@/devices/virtual/switch/h2w
186 * ACTION=change
187 * DEVPATH=/devices/virtual/switch/h2w
188 * SUBSYSTEM=switch
189 * SWITCH_NAME=Headset
190 * SWITCH_STATE=1 // Headset with mic
191 * SEQNUM=1602
193 class SwitchHandlerHeadphone: public SwitchHandler
195 public:
196 SwitchHandlerHeadphone(const char* aDevPath) :
197 SwitchHandler(aDevPath, SWITCH_HEADPHONES)
199 SwitchHandler::GetInitialState();
202 virtual ~SwitchHandlerHeadphone() { }
204 protected:
205 SwitchState ConvertState(const char* aState)
207 MOZ_ASSERT(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
219 public:
220 SwitchEventRunnable(SwitchEvent& aEvent) : mEvent(aEvent)
224 NS_IMETHOD Run()
226 NotifySwitchChange(mEvent);
227 return NS_OK;
229 private:
230 SwitchEvent mEvent;
233 class SwitchEventObserver MOZ_FINAL : public IUeventObserver
235 ~SwitchEventObserver()
237 mHandler.Clear();
240 public:
241 NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver)
242 SwitchEventObserver()
243 : mEnableCount(0),
244 mHeadphonesFromInputDev(false)
246 Init();
249 int GetEnableCount()
251 return mEnableCount;
254 void EnableSwitch(SwitchDevice aDevice)
256 mEventInfo[aDevice].mEnabled = true;
257 mEnableCount++;
260 void DisableSwitch(SwitchDevice aDevice)
262 mEventInfo[aDevice].mEnabled = false;
263 mEnableCount--;
266 void Notify(const NetlinkEvent& aEvent)
268 SwitchState currState;
270 SwitchDevice device = GetEventInfo(aEvent, currState);
271 if (device == SWITCH_DEVICE_UNKNOWN) {
272 return;
275 EventInfo& info = mEventInfo[device];
276 if (currState == info.mEvent.status()) {
277 return;
280 info.mEvent.status() = currState;
282 if (info.mEnabled) {
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()) {
291 return;
294 info.mEvent.status() = aState;
296 if (info.mEnabled) {
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;
319 private:
320 class EventInfo
322 public:
323 EventInfo() : mEnabled(false)
325 mEvent.status() = SWITCH_STATE_UNKNOWN;
326 mEvent.device() = SWITCH_DEVICE_UNKNOWN;
328 SwitchEvent mEvent;
329 bool mEnabled;
332 EventInfo mEventInfo[NUM_SWITCH_DEVICE];
333 size_t mEnableCount;
334 SwitchHandlerArray mHandler;
335 bool mHeadphonesFromInputDev;
337 // This function might also get called on the main thread
338 // (from IsHeadphoneEventFromInputDev)
339 void Init()
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);
349 } else {
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) {
364 continue;
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;
390 static void
391 InitializeResourceIfNeed()
393 if (!sSwitchObserver) {
394 sSwitchObserver = new SwitchEventObserver();
395 RegisterUeventListener(sSwitchObserver);
399 static void
400 ReleaseResourceIfNeed()
402 if (sSwitchObserver->GetEnableCount() == 0) {
403 UnregisterUeventListener(sSwitchObserver);
404 sSwitchObserver = nullptr;
408 static void
409 EnableSwitchNotificationsIOThread(SwitchDevice aDevice, Monitor *aMonitor)
411 InitializeResourceIfNeed();
412 sSwitchObserver->EnableSwitch(aDevice);
414 MonitorAutoLock lock(*aMonitor);
415 lock.Notify();
418 // Notify the latest state if IO thread has the information.
419 if (sSwitchObserver->GetEnableCount() > 1) {
420 sSwitchObserver->NotifyAnEvent(aDevice);
424 void
425 EnableSwitchNotifications(SwitchDevice aDevice)
427 Monitor monitor("EnableSwitch.monitor");
429 MonitorAutoLock lock(monitor);
430 XRE_GetIOMessageLoop()->PostTask(
431 FROM_HERE,
432 NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice, &monitor));
433 lock.Wait();
437 static void
438 DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
440 MOZ_ASSERT(sSwitchObserver->GetEnableCount());
441 sSwitchObserver->DisableSwitch(aDevice);
442 ReleaseResourceIfNeed();
445 void
446 DisableSwitchNotifications(SwitchDevice aDevice)
448 XRE_GetIOMessageLoop()->PostTask(
449 FROM_HERE,
450 NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
453 SwitchState
454 GetCurrentSwitchState(SwitchDevice aDevice)
456 MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
457 return sSwitchObserver->GetCurrentInformation(aDevice);
460 static void
461 NotifySwitchStateIOThread(SwitchDevice aDevice, SwitchState aState)
463 InitializeResourceIfNeed();
464 sSwitchObserver->Notify(aDevice, aState);
467 void NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
469 XRE_GetIOMessageLoop()->PostTask(
470 FROM_HERE,
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();
482 } // hal_impl
483 } //mozilla