1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/hal/gonk/GonkSwitch.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,462 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* Copyright 2012 Mozilla Foundation and Mozilla contributors 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * Unless required by applicable law or agreed to in writing, software 1.14 + * distributed under the License is distributed on an "AS IS" BASIS, 1.15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.16 + * See the License for the specific language governing permissions and 1.17 + * limitations under the License. 1.18 + */ 1.19 + 1.20 +#include <android/log.h> 1.21 +#include <fcntl.h> 1.22 +#include <sysutils/NetlinkEvent.h> 1.23 +#include <cutils/properties.h> 1.24 + 1.25 +#include "base/message_loop.h" 1.26 + 1.27 +#include "Hal.h" 1.28 +#include "mozilla/FileUtils.h" 1.29 +#include "mozilla/RefPtr.h" 1.30 +#include "mozilla/Monitor.h" 1.31 +#include "nsPrintfCString.h" 1.32 +#include "nsXULAppAPI.h" 1.33 +#include "nsThreadUtils.h" 1.34 +#include "UeventPoller.h" 1.35 + 1.36 +using namespace mozilla::hal; 1.37 + 1.38 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkSwitch" , ## args) 1.39 + 1.40 +#define SWITCH_HEADSET_DEVPATH "/devices/virtual/switch/h2w" 1.41 +#define SWITCH_USB_DEVPATH_GB "/devices/virtual/switch/usb_configuration" 1.42 +#define SWITCH_USB_DEVPATH_ICS "/devices/virtual/android_usb/android0" 1.43 + 1.44 +namespace mozilla { 1.45 +namespace hal_impl { 1.46 +/** 1.47 + * The uevent for a usb on GB insertion looks like: 1.48 + * 1.49 + * change@/devices/virtual/switch/usb_configuration 1.50 + * ACTION=change 1.51 + * DEVPATH=/devices/virtual/switch/usb_configuration 1.52 + * SUBSYSTEM=switch 1.53 + * SWITCH_NAME=usb_configuration 1.54 + * SWITCH_STATE=0 1.55 + * SEQNUM=5038 1.56 + */ 1.57 +class SwitchHandler 1.58 +{ 1.59 +public: 1.60 + NS_INLINE_DECL_REFCOUNTING(SwitchHandler) 1.61 + 1.62 + SwitchHandler(const char* aDevPath, SwitchDevice aDevice) 1.63 + : mDevPath(aDevPath), 1.64 + mState(SWITCH_STATE_UNKNOWN), 1.65 + mDevice(aDevice) 1.66 + { 1.67 + GetInitialState(); 1.68 + } 1.69 + 1.70 + virtual ~SwitchHandler() 1.71 + { 1.72 + } 1.73 + 1.74 + bool CheckEvent(NetlinkEvent* aEvent) 1.75 + { 1.76 + if (strcmp(GetSubsystem(), aEvent->getSubsystem()) || 1.77 + strcmp(mDevPath, aEvent->findParam("DEVPATH"))) { 1.78 + return false; 1.79 + } 1.80 + 1.81 + mState = ConvertState(GetStateString(aEvent)); 1.82 + return mState != SWITCH_STATE_UNKNOWN; 1.83 + } 1.84 + 1.85 + SwitchState GetState() 1.86 + { 1.87 + return mState; 1.88 + } 1.89 + 1.90 + SwitchDevice GetType() 1.91 + { 1.92 + return mDevice; 1.93 + } 1.94 +protected: 1.95 + virtual const char* GetSubsystem() 1.96 + { 1.97 + return "switch"; 1.98 + } 1.99 + 1.100 + virtual const char* GetStateString(NetlinkEvent* aEvent) 1.101 + { 1.102 + return aEvent->findParam("SWITCH_STATE"); 1.103 + } 1.104 + 1.105 + void GetInitialState() 1.106 + { 1.107 + nsPrintfCString statePath("/sys%s/state", mDevPath); 1.108 + int fd = open(statePath.get(), O_RDONLY); 1.109 + if (fd <= 0) { 1.110 + return; 1.111 + } 1.112 + 1.113 + ScopedClose autoClose(fd); 1.114 + char state[16]; 1.115 + ssize_t bytesRead = read(fd, state, sizeof(state)); 1.116 + if (bytesRead < 0) { 1.117 + LOG("Read data from %s fails", statePath.get()); 1.118 + return; 1.119 + } 1.120 + 1.121 + if (state[bytesRead - 1] == '\n') { 1.122 + bytesRead--; 1.123 + } 1.124 + 1.125 + state[bytesRead] = '\0'; 1.126 + mState = ConvertState(state); 1.127 + } 1.128 + 1.129 + virtual SwitchState ConvertState(const char* aState) 1.130 + { 1.131 + MOZ_ASSERT(aState); 1.132 + return aState[0] == '0' ? SWITCH_STATE_OFF : SWITCH_STATE_ON; 1.133 + } 1.134 + 1.135 + const char* mDevPath; 1.136 + SwitchState mState; 1.137 + SwitchDevice mDevice; 1.138 +}; 1.139 + 1.140 +/** 1.141 + * The uevent delivered for the USB configuration under ICS looks like, 1.142 + * 1.143 + * change@/devices/virtual/android_usb/android0 1.144 + * ACTION=change 1.145 + * DEVPATH=/devices/virtual/android_usb/android0 1.146 + * SUBSYSTEM=android_usb 1.147 + * USB_STATE=CONFIGURED 1.148 + * SEQNUM=1802 1.149 + */ 1.150 +class SwitchHandlerUsbIcs: public SwitchHandler 1.151 +{ 1.152 +public: 1.153 + SwitchHandlerUsbIcs(const char* aDevPath) : SwitchHandler(aDevPath, SWITCH_USB) 1.154 + { 1.155 + SwitchHandler::GetInitialState(); 1.156 + } 1.157 + 1.158 + virtual ~SwitchHandlerUsbIcs() { } 1.159 + 1.160 +protected: 1.161 + virtual const char* GetSubsystem() 1.162 + { 1.163 + return "android_usb"; 1.164 + } 1.165 + 1.166 + virtual const char* GetStateString(NetlinkEvent* aEvent) 1.167 + { 1.168 + return aEvent->findParam("USB_STATE"); 1.169 + } 1.170 + 1.171 + SwitchState ConvertState(const char* aState) 1.172 + { 1.173 + MOZ_ASSERT(aState); 1.174 + return strcmp(aState, "CONFIGURED") == 0 ? SWITCH_STATE_ON : SWITCH_STATE_OFF; 1.175 + } 1.176 +}; 1.177 + 1.178 +/** 1.179 + * The uevent delivered for the headset under ICS looks like, 1.180 + * 1.181 + * change@/devices/virtual/switch/h2w 1.182 + * ACTION=change 1.183 + * DEVPATH=/devices/virtual/switch/h2w 1.184 + * SUBSYSTEM=switch 1.185 + * SWITCH_NAME=h2w 1.186 + * SWITCH_STATE=2 // Headset with no mic 1.187 + * SEQNUM=2581 1.188 + * On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug. 1.189 + * change@/devices/virtual/switch/h2w 1.190 + * ACTION=change 1.191 + * DEVPATH=/devices/virtual/switch/h2w 1.192 + * SUBSYSTEM=switch 1.193 + * SWITCH_NAME=Headset 1.194 + * SWITCH_STATE=1 // Headset with mic 1.195 + * SEQNUM=1602 1.196 + */ 1.197 +class SwitchHandlerHeadphone: public SwitchHandler 1.198 +{ 1.199 +public: 1.200 + SwitchHandlerHeadphone(const char* aDevPath) : 1.201 + SwitchHandler(aDevPath, SWITCH_HEADPHONES) 1.202 + { 1.203 + SwitchHandler::GetInitialState(); 1.204 + } 1.205 + 1.206 + virtual ~SwitchHandlerHeadphone() { } 1.207 + 1.208 +protected: 1.209 + SwitchState ConvertState(const char* aState) 1.210 + { 1.211 + MOZ_ASSERT(aState); 1.212 + 1.213 + return aState[0] == '0' ? SWITCH_STATE_OFF : 1.214 + (aState[0] == '1' ? SWITCH_STATE_HEADSET : SWITCH_STATE_HEADPHONE); 1.215 + } 1.216 +}; 1.217 + 1.218 + 1.219 +typedef nsTArray<RefPtr<SwitchHandler> > SwitchHandlerArray; 1.220 + 1.221 +class SwitchEventRunnable : public nsRunnable 1.222 +{ 1.223 +public: 1.224 + SwitchEventRunnable(SwitchEvent& aEvent) : mEvent(aEvent) 1.225 + { 1.226 + } 1.227 + 1.228 + NS_IMETHOD Run() 1.229 + { 1.230 + NotifySwitchChange(mEvent); 1.231 + return NS_OK; 1.232 + } 1.233 +private: 1.234 + SwitchEvent mEvent; 1.235 +}; 1.236 + 1.237 +class SwitchEventObserver MOZ_FINAL : public IUeventObserver 1.238 +{ 1.239 + ~SwitchEventObserver() 1.240 + { 1.241 + mHandler.Clear(); 1.242 + } 1.243 + 1.244 +public: 1.245 + NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver) 1.246 + SwitchEventObserver() : mEnableCount(0) 1.247 + { 1.248 + Init(); 1.249 + } 1.250 + 1.251 + int GetEnableCount() 1.252 + { 1.253 + return mEnableCount; 1.254 + } 1.255 + 1.256 + void EnableSwitch(SwitchDevice aDevice) 1.257 + { 1.258 + mEventInfo[aDevice].mEnabled = true; 1.259 + mEnableCount++; 1.260 + } 1.261 + 1.262 + void DisableSwitch(SwitchDevice aDevice) 1.263 + { 1.264 + mEventInfo[aDevice].mEnabled = false; 1.265 + mEnableCount--; 1.266 + } 1.267 + 1.268 + void Notify(const NetlinkEvent& aEvent) 1.269 + { 1.270 + SwitchState currState; 1.271 + 1.272 + SwitchDevice device = GetEventInfo(aEvent, currState); 1.273 + if (device == SWITCH_DEVICE_UNKNOWN) { 1.274 + return; 1.275 + } 1.276 + 1.277 + EventInfo& info = mEventInfo[device]; 1.278 + if (currState == info.mEvent.status()) { 1.279 + return; 1.280 + } 1.281 + 1.282 + info.mEvent.status() = currState; 1.283 + 1.284 + if (info.mEnabled) { 1.285 + NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent)); 1.286 + } 1.287 + } 1.288 + 1.289 + void Notify(SwitchDevice aDevice, SwitchState aState) 1.290 + { 1.291 + EventInfo& info = mEventInfo[aDevice]; 1.292 + if (aState == info.mEvent.status()) { 1.293 + return; 1.294 + } 1.295 + 1.296 + info.mEvent.status() = aState; 1.297 + 1.298 + if (info.mEnabled) { 1.299 + NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent)); 1.300 + } 1.301 + } 1.302 + 1.303 + SwitchState GetCurrentInformation(SwitchDevice aDevice) 1.304 + { 1.305 + return mEventInfo[aDevice].mEvent.status(); 1.306 + } 1.307 + 1.308 + void NotifyAnEvent(SwitchDevice aDevice) 1.309 + { 1.310 + EventInfo& info = mEventInfo[aDevice]; 1.311 + if (info.mEvent.status() != SWITCH_STATE_UNKNOWN) { 1.312 + NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent)); 1.313 + } 1.314 + } 1.315 +private: 1.316 + class EventInfo 1.317 + { 1.318 + public: 1.319 + EventInfo() : mEnabled(false) 1.320 + { 1.321 + mEvent.status() = SWITCH_STATE_UNKNOWN; 1.322 + mEvent.device() = SWITCH_DEVICE_UNKNOWN; 1.323 + } 1.324 + SwitchEvent mEvent; 1.325 + bool mEnabled; 1.326 + }; 1.327 + 1.328 + EventInfo mEventInfo[NUM_SWITCH_DEVICE]; 1.329 + size_t mEnableCount; 1.330 + SwitchHandlerArray mHandler; 1.331 + 1.332 + void Init() 1.333 + { 1.334 + char value[PROPERTY_VALUE_MAX]; 1.335 + property_get("ro.moz.devinputjack", value, "0"); 1.336 + bool headphonesFromInputDev = !strcmp(value, "1"); 1.337 + 1.338 + if (!headphonesFromInputDev) { 1.339 + mHandler.AppendElement(new SwitchHandlerHeadphone(SWITCH_HEADSET_DEVPATH)); 1.340 + } else { 1.341 + // If headphone status will be notified from input dev then initialize 1.342 + // status to "off" and wait for event notification. 1.343 + mEventInfo[SWITCH_HEADPHONES].mEvent.device() = SWITCH_HEADPHONES; 1.344 + mEventInfo[SWITCH_HEADPHONES].mEvent.status() = SWITCH_STATE_OFF; 1.345 + } 1.346 + mHandler.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB, SWITCH_USB)); 1.347 + mHandler.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS)); 1.348 + 1.349 + SwitchHandlerArray::index_type handlerIndex; 1.350 + SwitchHandlerArray::size_type numHandlers = mHandler.Length(); 1.351 + 1.352 + for (handlerIndex = 0; handlerIndex < numHandlers; handlerIndex++) { 1.353 + SwitchState state = mHandler[handlerIndex]->GetState(); 1.354 + if (state == SWITCH_STATE_UNKNOWN) { 1.355 + continue; 1.356 + } 1.357 + 1.358 + SwitchDevice device = mHandler[handlerIndex]->GetType(); 1.359 + mEventInfo[device].mEvent.device() = device; 1.360 + mEventInfo[device].mEvent.status() = state; 1.361 + } 1.362 + } 1.363 + 1.364 + SwitchDevice GetEventInfo(const NetlinkEvent& aEvent, SwitchState& aState) 1.365 + { 1.366 + //working around the android code not being const-correct 1.367 + NetlinkEvent *e = const_cast<NetlinkEvent*>(&aEvent); 1.368 + 1.369 + for (size_t i = 0; i < mHandler.Length(); i++) { 1.370 + if (mHandler[i]->CheckEvent(e)) { 1.371 + aState = mHandler[i]->GetState(); 1.372 + return mHandler[i]->GetType(); 1.373 + } 1.374 + } 1.375 + return SWITCH_DEVICE_UNKNOWN; 1.376 + } 1.377 +}; 1.378 + 1.379 +static RefPtr<SwitchEventObserver> sSwitchObserver; 1.380 + 1.381 +static void 1.382 +InitializeResourceIfNeed() 1.383 +{ 1.384 + if (!sSwitchObserver) { 1.385 + sSwitchObserver = new SwitchEventObserver(); 1.386 + RegisterUeventListener(sSwitchObserver); 1.387 + } 1.388 +} 1.389 + 1.390 +static void 1.391 +ReleaseResourceIfNeed() 1.392 +{ 1.393 + if (sSwitchObserver->GetEnableCount() == 0) { 1.394 + UnregisterUeventListener(sSwitchObserver); 1.395 + sSwitchObserver = nullptr; 1.396 + } 1.397 +} 1.398 + 1.399 +static void 1.400 +EnableSwitchNotificationsIOThread(SwitchDevice aDevice, Monitor *aMonitor) 1.401 +{ 1.402 + InitializeResourceIfNeed(); 1.403 + sSwitchObserver->EnableSwitch(aDevice); 1.404 + { 1.405 + MonitorAutoLock lock(*aMonitor); 1.406 + lock.Notify(); 1.407 + } 1.408 + 1.409 + // Notify the latest state if IO thread has the information. 1.410 + if (sSwitchObserver->GetEnableCount() > 1) { 1.411 + sSwitchObserver->NotifyAnEvent(aDevice); 1.412 + } 1.413 +} 1.414 + 1.415 +void 1.416 +EnableSwitchNotifications(SwitchDevice aDevice) 1.417 +{ 1.418 + Monitor monitor("EnableSwitch.monitor"); 1.419 + { 1.420 + MonitorAutoLock lock(monitor); 1.421 + XRE_GetIOMessageLoop()->PostTask( 1.422 + FROM_HERE, 1.423 + NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice, &monitor)); 1.424 + lock.Wait(); 1.425 + } 1.426 +} 1.427 + 1.428 +static void 1.429 +DisableSwitchNotificationsIOThread(SwitchDevice aDevice) 1.430 +{ 1.431 + MOZ_ASSERT(sSwitchObserver->GetEnableCount()); 1.432 + sSwitchObserver->DisableSwitch(aDevice); 1.433 + ReleaseResourceIfNeed(); 1.434 +} 1.435 + 1.436 +void 1.437 +DisableSwitchNotifications(SwitchDevice aDevice) 1.438 +{ 1.439 + XRE_GetIOMessageLoop()->PostTask( 1.440 + FROM_HERE, 1.441 + NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice)); 1.442 +} 1.443 + 1.444 +SwitchState 1.445 +GetCurrentSwitchState(SwitchDevice aDevice) 1.446 +{ 1.447 + MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount()); 1.448 + return sSwitchObserver->GetCurrentInformation(aDevice); 1.449 +} 1.450 + 1.451 +static void 1.452 +NotifySwitchStateIOThread(SwitchDevice aDevice, SwitchState aState) 1.453 +{ 1.454 + sSwitchObserver->Notify(aDevice, aState); 1.455 +} 1.456 + 1.457 +void NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState) 1.458 +{ 1.459 + InitializeResourceIfNeed(); 1.460 + XRE_GetIOMessageLoop()->PostTask( 1.461 + FROM_HERE, 1.462 + NewRunnableFunction(NotifySwitchStateIOThread, aDevice, aState)); 1.463 +} 1.464 +} // hal_impl 1.465 +} //mozilla