Backed out 6 changesets (bug 854169) to investigate OSX Mn bustage on a CLOSED TREE.
[gecko.git] / hal / gonk / GonkSwitch.cpp
blob55eecf0e640799cbcd2bf7763b8c0c4215ae9ef5
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 : public RefCounted<SwitchHandler>
55 public:
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 LOG("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 : public IUeventObserver,
232 public RefCounted<SwitchEventObserver>
234 public:
235 SwitchEventObserver() : mEnableCount(0)
237 Init();
240 ~SwitchEventObserver()
242 mHandler.Clear();
245 int GetEnableCount()
247 return mEnableCount;
250 void EnableSwitch(SwitchDevice aDevice)
252 mEventInfo[aDevice].mEnabled = true;
253 mEnableCount++;
256 void DisableSwitch(SwitchDevice aDevice)
258 mEventInfo[aDevice].mEnabled = false;
259 mEnableCount--;
262 void Notify(const NetlinkEvent& aEvent)
264 SwitchState currState;
266 SwitchDevice device = GetEventInfo(aEvent, currState);
267 if (device == SWITCH_DEVICE_UNKNOWN) {
268 return;
271 EventInfo& info = mEventInfo[device];
272 if (currState == info.mEvent.status()) {
273 return;
276 info.mEvent.status() = currState;
278 if (info.mEnabled) {
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));
295 private:
296 class EventInfo
298 public:
299 EventInfo() : mEnabled(false)
301 mEvent.status() = SWITCH_STATE_UNKNOWN;
302 mEvent.device() = SWITCH_DEVICE_UNKNOWN;
304 SwitchEvent mEvent;
305 bool mEnabled;
308 EventInfo mEventInfo[NUM_SWITCH_DEVICE];
309 size_t mEnableCount;
310 SwitchHandlerArray mHandler;
312 void Init()
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) {
324 continue;
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;
350 static void
351 InitializeResourceIfNeed()
353 if (!sSwitchObserver) {
354 sSwitchObserver = new SwitchEventObserver();
355 RegisterUeventListener(sSwitchObserver);
359 static void
360 ReleaseResourceIfNeed()
362 if (sSwitchObserver->GetEnableCount() == 0) {
363 UnregisterUeventListener(sSwitchObserver);
364 sSwitchObserver = nullptr;
368 static void
369 EnableSwitchNotificationsIOThread(SwitchDevice aDevice, Monitor *aMonitor)
371 InitializeResourceIfNeed();
372 sSwitchObserver->EnableSwitch(aDevice);
374 MonitorAutoLock lock(*aMonitor);
375 lock.Notify();
378 // Notify the latest state if IO thread has the information.
379 if (sSwitchObserver->GetEnableCount() > 1) {
380 sSwitchObserver->NotifyAnEvent(aDevice);
384 void
385 EnableSwitchNotifications(SwitchDevice aDevice)
387 Monitor monitor("EnableSwitch.monitor");
389 MonitorAutoLock lock(monitor);
390 XRE_GetIOMessageLoop()->PostTask(
391 FROM_HERE,
392 NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice, &monitor));
393 lock.Wait();
397 static void
398 DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
400 MOZ_ASSERT(sSwitchObserver->GetEnableCount());
401 sSwitchObserver->DisableSwitch(aDevice);
402 ReleaseResourceIfNeed();
405 void
406 DisableSwitchNotifications(SwitchDevice aDevice)
408 XRE_GetIOMessageLoop()->PostTask(
409 FROM_HERE,
410 NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
413 SwitchState
414 GetCurrentSwitchState(SwitchDevice aDevice)
416 MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
417 return sSwitchObserver->GetCurrentInformation(aDevice);
420 } // hal_impl
421 } //mozilla