Bumping manifests a=b2g-bump
[gecko.git] / hal / gonk / GonkSwitch.cpp
blob65c9e1b6b2e6500c7dfbf38335a3e51aaacbabda
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 <fcntl.h>
18 #include <sysutils/NetlinkEvent.h>
20 #include "base/message_loop.h"
22 #include "Hal.h"
23 #include "HalLog.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"
38 namespace mozilla {
39 namespace hal_impl {
40 /**
41 * The uevent for a usb on GB insertion looks like:
43 * change@/devices/virtual/switch/usb_configuration
44 * ACTION=change
45 * DEVPATH=/devices/virtual/switch/usb_configuration
46 * SUBSYSTEM=switch
47 * SWITCH_NAME=usb_configuration
48 * SWITCH_STATE=0
49 * SEQNUM=5038
51 class SwitchHandler
53 public:
54 NS_INLINE_DECL_REFCOUNTING(SwitchHandler)
56 SwitchHandler(const char* aDevPath, SwitchDevice aDevice)
57 : mDevPath(aDevPath),
58 mState(SWITCH_STATE_UNKNOWN),
59 mDevice(aDevice)
61 GetInitialState();
64 virtual ~SwitchHandler()
68 bool CheckEvent(NetlinkEvent* aEvent)
70 if (strcmp(GetSubsystem(), aEvent->getSubsystem()) ||
71 strcmp(mDevPath, aEvent->findParam("DEVPATH"))) {
72 return false;
75 mState = ConvertState(GetStateString(aEvent));
76 return mState != SWITCH_STATE_UNKNOWN;
79 SwitchState GetState()
81 return mState;
84 SwitchDevice GetType()
86 return mDevice;
88 protected:
89 virtual const char* GetSubsystem()
91 return "switch";
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);
103 if (fd <= 0) {
104 return;
107 ScopedClose autoClose(fd);
108 char state[16];
109 ssize_t bytesRead = read(fd, state, sizeof(state));
110 if (bytesRead < 0) {
111 HAL_ERR("Read data from %s fails", statePath.get());
112 return;
115 if (state[bytesRead - 1] == '\n') {
116 bytesRead--;
119 state[bytesRead] = '\0';
120 mState = ConvertState(state);
123 virtual SwitchState ConvertState(const char* aState)
125 MOZ_ASSERT(aState);
126 return aState[0] == '0' ? SWITCH_STATE_OFF : SWITCH_STATE_ON;
129 const char* mDevPath;
130 SwitchState mState;
131 SwitchDevice mDevice;
135 * The uevent delivered for the USB configuration under ICS looks like,
137 * change@/devices/virtual/android_usb/android0
138 * ACTION=change
139 * DEVPATH=/devices/virtual/android_usb/android0
140 * SUBSYSTEM=android_usb
141 * USB_STATE=CONFIGURED
142 * SEQNUM=1802
144 class SwitchHandlerUsbIcs: public SwitchHandler
146 public:
147 SwitchHandlerUsbIcs(const char* aDevPath) : SwitchHandler(aDevPath, SWITCH_USB)
149 SwitchHandler::GetInitialState();
152 virtual ~SwitchHandlerUsbIcs() { }
154 protected:
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)
167 MOZ_ASSERT(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
176 * ACTION=change
177 * DEVPATH=/devices/virtual/switch/h2w
178 * SUBSYSTEM=switch
179 * SWITCH_NAME=h2w
180 * SWITCH_STATE=2 // Headset with no mic
181 * SEQNUM=2581
182 * On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug.
183 * change@/devices/virtual/switch/h2w
184 * ACTION=change
185 * DEVPATH=/devices/virtual/switch/h2w
186 * SUBSYSTEM=switch
187 * SWITCH_NAME=Headset
188 * SWITCH_STATE=1 // Headset with mic
189 * SEQNUM=1602
191 class SwitchHandlerHeadphone: public SwitchHandler
193 public:
194 SwitchHandlerHeadphone(const char* aDevPath) :
195 SwitchHandler(aDevPath, SWITCH_HEADPHONES)
197 SwitchHandler::GetInitialState();
200 virtual ~SwitchHandlerHeadphone() { }
202 protected:
203 SwitchState ConvertState(const char* aState)
205 MOZ_ASSERT(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
217 public:
218 SwitchEventRunnable(SwitchEvent& aEvent) : mEvent(aEvent)
222 NS_IMETHOD Run()
224 NotifySwitchChange(mEvent);
225 return NS_OK;
227 private:
228 SwitchEvent mEvent;
231 class SwitchEventObserver MOZ_FINAL : public IUeventObserver
233 ~SwitchEventObserver()
235 mHandler.Clear();
238 public:
239 NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver)
240 SwitchEventObserver()
241 : mEnableCount(0),
242 mHeadphonesFromInputDev(false)
244 Init();
247 int GetEnableCount()
249 return mEnableCount;
252 void EnableSwitch(SwitchDevice aDevice)
254 mEventInfo[aDevice].mEnabled = true;
255 mEnableCount++;
258 void DisableSwitch(SwitchDevice aDevice)
260 mEventInfo[aDevice].mEnabled = false;
261 mEnableCount--;
264 void Notify(const NetlinkEvent& aEvent)
266 SwitchState currState;
268 SwitchDevice device = GetEventInfo(aEvent, currState);
269 if (device == SWITCH_DEVICE_UNKNOWN) {
270 return;
273 EventInfo& info = mEventInfo[device];
274 if (currState == info.mEvent.status()) {
275 return;
278 info.mEvent.status() = currState;
280 if (info.mEnabled) {
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()) {
289 return;
292 info.mEvent.status() = aState;
294 if (info.mEnabled) {
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;
317 private:
318 class EventInfo
320 public:
321 EventInfo() : mEnabled(false)
323 mEvent.status() = SWITCH_STATE_UNKNOWN;
324 mEvent.device() = SWITCH_DEVICE_UNKNOWN;
326 SwitchEvent mEvent;
327 bool mEnabled;
330 EventInfo mEventInfo[NUM_SWITCH_DEVICE];
331 size_t mEnableCount;
332 SwitchHandlerArray mHandler;
333 bool mHeadphonesFromInputDev;
335 // This function might also get called on the main thread
336 // (from IsHeadphoneEventFromInputDev)
337 void Init()
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);
347 } else {
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) {
362 continue;
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;
388 static void
389 InitializeResourceIfNeed()
391 if (!sSwitchObserver) {
392 sSwitchObserver = new SwitchEventObserver();
393 RegisterUeventListener(sSwitchObserver);
397 static void
398 ReleaseResourceIfNeed()
400 if (sSwitchObserver->GetEnableCount() == 0) {
401 UnregisterUeventListener(sSwitchObserver);
402 sSwitchObserver = nullptr;
406 static void
407 EnableSwitchNotificationsIOThread(SwitchDevice aDevice, Monitor *aMonitor)
409 InitializeResourceIfNeed();
410 sSwitchObserver->EnableSwitch(aDevice);
412 MonitorAutoLock lock(*aMonitor);
413 lock.Notify();
416 // Notify the latest state if IO thread has the information.
417 if (sSwitchObserver->GetEnableCount() > 1) {
418 sSwitchObserver->NotifyAnEvent(aDevice);
422 void
423 EnableSwitchNotifications(SwitchDevice aDevice)
425 Monitor monitor("EnableSwitch.monitor");
427 MonitorAutoLock lock(monitor);
428 XRE_GetIOMessageLoop()->PostTask(
429 FROM_HERE,
430 NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice, &monitor));
431 lock.Wait();
435 static void
436 DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
438 MOZ_ASSERT(sSwitchObserver->GetEnableCount());
439 sSwitchObserver->DisableSwitch(aDevice);
440 ReleaseResourceIfNeed();
443 void
444 DisableSwitchNotifications(SwitchDevice aDevice)
446 XRE_GetIOMessageLoop()->PostTask(
447 FROM_HERE,
448 NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
451 SwitchState
452 GetCurrentSwitchState(SwitchDevice aDevice)
454 MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
455 return sSwitchObserver->GetCurrentInformation(aDevice);
458 static void
459 NotifySwitchStateIOThread(SwitchDevice aDevice, SwitchState aState)
461 InitializeResourceIfNeed();
462 sSwitchObserver->Notify(aDevice, aState);
465 void NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
467 XRE_GetIOMessageLoop()->PostTask(
468 FROM_HERE,
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();
480 } // hal_impl
481 } //mozilla