hal/gonk/GonkSwitch.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
michael@0 3 *
michael@0 4 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 5 * you may not use this file except in compliance with the License.
michael@0 6 * You may obtain a copy of the License at
michael@0 7 *
michael@0 8 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 9 *
michael@0 10 * Unless required by applicable law or agreed to in writing, software
michael@0 11 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 13 * See the License for the specific language governing permissions and
michael@0 14 * limitations under the License.
michael@0 15 */
michael@0 16
michael@0 17 #include <android/log.h>
michael@0 18 #include <fcntl.h>
michael@0 19 #include <sysutils/NetlinkEvent.h>
michael@0 20 #include <cutils/properties.h>
michael@0 21
michael@0 22 #include "base/message_loop.h"
michael@0 23
michael@0 24 #include "Hal.h"
michael@0 25 #include "mozilla/FileUtils.h"
michael@0 26 #include "mozilla/RefPtr.h"
michael@0 27 #include "mozilla/Monitor.h"
michael@0 28 #include "nsPrintfCString.h"
michael@0 29 #include "nsXULAppAPI.h"
michael@0 30 #include "nsThreadUtils.h"
michael@0 31 #include "UeventPoller.h"
michael@0 32
michael@0 33 using namespace mozilla::hal;
michael@0 34
michael@0 35 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkSwitch" , ## args)
michael@0 36
michael@0 37 #define SWITCH_HEADSET_DEVPATH "/devices/virtual/switch/h2w"
michael@0 38 #define SWITCH_USB_DEVPATH_GB "/devices/virtual/switch/usb_configuration"
michael@0 39 #define SWITCH_USB_DEVPATH_ICS "/devices/virtual/android_usb/android0"
michael@0 40
michael@0 41 namespace mozilla {
michael@0 42 namespace hal_impl {
michael@0 43 /**
michael@0 44 * The uevent for a usb on GB insertion looks like:
michael@0 45 *
michael@0 46 * change@/devices/virtual/switch/usb_configuration
michael@0 47 * ACTION=change
michael@0 48 * DEVPATH=/devices/virtual/switch/usb_configuration
michael@0 49 * SUBSYSTEM=switch
michael@0 50 * SWITCH_NAME=usb_configuration
michael@0 51 * SWITCH_STATE=0
michael@0 52 * SEQNUM=5038
michael@0 53 */
michael@0 54 class SwitchHandler
michael@0 55 {
michael@0 56 public:
michael@0 57 NS_INLINE_DECL_REFCOUNTING(SwitchHandler)
michael@0 58
michael@0 59 SwitchHandler(const char* aDevPath, SwitchDevice aDevice)
michael@0 60 : mDevPath(aDevPath),
michael@0 61 mState(SWITCH_STATE_UNKNOWN),
michael@0 62 mDevice(aDevice)
michael@0 63 {
michael@0 64 GetInitialState();
michael@0 65 }
michael@0 66
michael@0 67 virtual ~SwitchHandler()
michael@0 68 {
michael@0 69 }
michael@0 70
michael@0 71 bool CheckEvent(NetlinkEvent* aEvent)
michael@0 72 {
michael@0 73 if (strcmp(GetSubsystem(), aEvent->getSubsystem()) ||
michael@0 74 strcmp(mDevPath, aEvent->findParam("DEVPATH"))) {
michael@0 75 return false;
michael@0 76 }
michael@0 77
michael@0 78 mState = ConvertState(GetStateString(aEvent));
michael@0 79 return mState != SWITCH_STATE_UNKNOWN;
michael@0 80 }
michael@0 81
michael@0 82 SwitchState GetState()
michael@0 83 {
michael@0 84 return mState;
michael@0 85 }
michael@0 86
michael@0 87 SwitchDevice GetType()
michael@0 88 {
michael@0 89 return mDevice;
michael@0 90 }
michael@0 91 protected:
michael@0 92 virtual const char* GetSubsystem()
michael@0 93 {
michael@0 94 return "switch";
michael@0 95 }
michael@0 96
michael@0 97 virtual const char* GetStateString(NetlinkEvent* aEvent)
michael@0 98 {
michael@0 99 return aEvent->findParam("SWITCH_STATE");
michael@0 100 }
michael@0 101
michael@0 102 void GetInitialState()
michael@0 103 {
michael@0 104 nsPrintfCString statePath("/sys%s/state", mDevPath);
michael@0 105 int fd = open(statePath.get(), O_RDONLY);
michael@0 106 if (fd <= 0) {
michael@0 107 return;
michael@0 108 }
michael@0 109
michael@0 110 ScopedClose autoClose(fd);
michael@0 111 char state[16];
michael@0 112 ssize_t bytesRead = read(fd, state, sizeof(state));
michael@0 113 if (bytesRead < 0) {
michael@0 114 LOG("Read data from %s fails", statePath.get());
michael@0 115 return;
michael@0 116 }
michael@0 117
michael@0 118 if (state[bytesRead - 1] == '\n') {
michael@0 119 bytesRead--;
michael@0 120 }
michael@0 121
michael@0 122 state[bytesRead] = '\0';
michael@0 123 mState = ConvertState(state);
michael@0 124 }
michael@0 125
michael@0 126 virtual SwitchState ConvertState(const char* aState)
michael@0 127 {
michael@0 128 MOZ_ASSERT(aState);
michael@0 129 return aState[0] == '0' ? SWITCH_STATE_OFF : SWITCH_STATE_ON;
michael@0 130 }
michael@0 131
michael@0 132 const char* mDevPath;
michael@0 133 SwitchState mState;
michael@0 134 SwitchDevice mDevice;
michael@0 135 };
michael@0 136
michael@0 137 /**
michael@0 138 * The uevent delivered for the USB configuration under ICS looks like,
michael@0 139 *
michael@0 140 * change@/devices/virtual/android_usb/android0
michael@0 141 * ACTION=change
michael@0 142 * DEVPATH=/devices/virtual/android_usb/android0
michael@0 143 * SUBSYSTEM=android_usb
michael@0 144 * USB_STATE=CONFIGURED
michael@0 145 * SEQNUM=1802
michael@0 146 */
michael@0 147 class SwitchHandlerUsbIcs: public SwitchHandler
michael@0 148 {
michael@0 149 public:
michael@0 150 SwitchHandlerUsbIcs(const char* aDevPath) : SwitchHandler(aDevPath, SWITCH_USB)
michael@0 151 {
michael@0 152 SwitchHandler::GetInitialState();
michael@0 153 }
michael@0 154
michael@0 155 virtual ~SwitchHandlerUsbIcs() { }
michael@0 156
michael@0 157 protected:
michael@0 158 virtual const char* GetSubsystem()
michael@0 159 {
michael@0 160 return "android_usb";
michael@0 161 }
michael@0 162
michael@0 163 virtual const char* GetStateString(NetlinkEvent* aEvent)
michael@0 164 {
michael@0 165 return aEvent->findParam("USB_STATE");
michael@0 166 }
michael@0 167
michael@0 168 SwitchState ConvertState(const char* aState)
michael@0 169 {
michael@0 170 MOZ_ASSERT(aState);
michael@0 171 return strcmp(aState, "CONFIGURED") == 0 ? SWITCH_STATE_ON : SWITCH_STATE_OFF;
michael@0 172 }
michael@0 173 };
michael@0 174
michael@0 175 /**
michael@0 176 * The uevent delivered for the headset under ICS looks like,
michael@0 177 *
michael@0 178 * change@/devices/virtual/switch/h2w
michael@0 179 * ACTION=change
michael@0 180 * DEVPATH=/devices/virtual/switch/h2w
michael@0 181 * SUBSYSTEM=switch
michael@0 182 * SWITCH_NAME=h2w
michael@0 183 * SWITCH_STATE=2 // Headset with no mic
michael@0 184 * SEQNUM=2581
michael@0 185 * On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug.
michael@0 186 * change@/devices/virtual/switch/h2w
michael@0 187 * ACTION=change
michael@0 188 * DEVPATH=/devices/virtual/switch/h2w
michael@0 189 * SUBSYSTEM=switch
michael@0 190 * SWITCH_NAME=Headset
michael@0 191 * SWITCH_STATE=1 // Headset with mic
michael@0 192 * SEQNUM=1602
michael@0 193 */
michael@0 194 class SwitchHandlerHeadphone: public SwitchHandler
michael@0 195 {
michael@0 196 public:
michael@0 197 SwitchHandlerHeadphone(const char* aDevPath) :
michael@0 198 SwitchHandler(aDevPath, SWITCH_HEADPHONES)
michael@0 199 {
michael@0 200 SwitchHandler::GetInitialState();
michael@0 201 }
michael@0 202
michael@0 203 virtual ~SwitchHandlerHeadphone() { }
michael@0 204
michael@0 205 protected:
michael@0 206 SwitchState ConvertState(const char* aState)
michael@0 207 {
michael@0 208 MOZ_ASSERT(aState);
michael@0 209
michael@0 210 return aState[0] == '0' ? SWITCH_STATE_OFF :
michael@0 211 (aState[0] == '1' ? SWITCH_STATE_HEADSET : SWITCH_STATE_HEADPHONE);
michael@0 212 }
michael@0 213 };
michael@0 214
michael@0 215
michael@0 216 typedef nsTArray<RefPtr<SwitchHandler> > SwitchHandlerArray;
michael@0 217
michael@0 218 class SwitchEventRunnable : public nsRunnable
michael@0 219 {
michael@0 220 public:
michael@0 221 SwitchEventRunnable(SwitchEvent& aEvent) : mEvent(aEvent)
michael@0 222 {
michael@0 223 }
michael@0 224
michael@0 225 NS_IMETHOD Run()
michael@0 226 {
michael@0 227 NotifySwitchChange(mEvent);
michael@0 228 return NS_OK;
michael@0 229 }
michael@0 230 private:
michael@0 231 SwitchEvent mEvent;
michael@0 232 };
michael@0 233
michael@0 234 class SwitchEventObserver MOZ_FINAL : public IUeventObserver
michael@0 235 {
michael@0 236 ~SwitchEventObserver()
michael@0 237 {
michael@0 238 mHandler.Clear();
michael@0 239 }
michael@0 240
michael@0 241 public:
michael@0 242 NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver)
michael@0 243 SwitchEventObserver() : mEnableCount(0)
michael@0 244 {
michael@0 245 Init();
michael@0 246 }
michael@0 247
michael@0 248 int GetEnableCount()
michael@0 249 {
michael@0 250 return mEnableCount;
michael@0 251 }
michael@0 252
michael@0 253 void EnableSwitch(SwitchDevice aDevice)
michael@0 254 {
michael@0 255 mEventInfo[aDevice].mEnabled = true;
michael@0 256 mEnableCount++;
michael@0 257 }
michael@0 258
michael@0 259 void DisableSwitch(SwitchDevice aDevice)
michael@0 260 {
michael@0 261 mEventInfo[aDevice].mEnabled = false;
michael@0 262 mEnableCount--;
michael@0 263 }
michael@0 264
michael@0 265 void Notify(const NetlinkEvent& aEvent)
michael@0 266 {
michael@0 267 SwitchState currState;
michael@0 268
michael@0 269 SwitchDevice device = GetEventInfo(aEvent, currState);
michael@0 270 if (device == SWITCH_DEVICE_UNKNOWN) {
michael@0 271 return;
michael@0 272 }
michael@0 273
michael@0 274 EventInfo& info = mEventInfo[device];
michael@0 275 if (currState == info.mEvent.status()) {
michael@0 276 return;
michael@0 277 }
michael@0 278
michael@0 279 info.mEvent.status() = currState;
michael@0 280
michael@0 281 if (info.mEnabled) {
michael@0 282 NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
michael@0 283 }
michael@0 284 }
michael@0 285
michael@0 286 void Notify(SwitchDevice aDevice, SwitchState aState)
michael@0 287 {
michael@0 288 EventInfo& info = mEventInfo[aDevice];
michael@0 289 if (aState == info.mEvent.status()) {
michael@0 290 return;
michael@0 291 }
michael@0 292
michael@0 293 info.mEvent.status() = aState;
michael@0 294
michael@0 295 if (info.mEnabled) {
michael@0 296 NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
michael@0 297 }
michael@0 298 }
michael@0 299
michael@0 300 SwitchState GetCurrentInformation(SwitchDevice aDevice)
michael@0 301 {
michael@0 302 return mEventInfo[aDevice].mEvent.status();
michael@0 303 }
michael@0 304
michael@0 305 void NotifyAnEvent(SwitchDevice aDevice)
michael@0 306 {
michael@0 307 EventInfo& info = mEventInfo[aDevice];
michael@0 308 if (info.mEvent.status() != SWITCH_STATE_UNKNOWN) {
michael@0 309 NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
michael@0 310 }
michael@0 311 }
michael@0 312 private:
michael@0 313 class EventInfo
michael@0 314 {
michael@0 315 public:
michael@0 316 EventInfo() : mEnabled(false)
michael@0 317 {
michael@0 318 mEvent.status() = SWITCH_STATE_UNKNOWN;
michael@0 319 mEvent.device() = SWITCH_DEVICE_UNKNOWN;
michael@0 320 }
michael@0 321 SwitchEvent mEvent;
michael@0 322 bool mEnabled;
michael@0 323 };
michael@0 324
michael@0 325 EventInfo mEventInfo[NUM_SWITCH_DEVICE];
michael@0 326 size_t mEnableCount;
michael@0 327 SwitchHandlerArray mHandler;
michael@0 328
michael@0 329 void Init()
michael@0 330 {
michael@0 331 char value[PROPERTY_VALUE_MAX];
michael@0 332 property_get("ro.moz.devinputjack", value, "0");
michael@0 333 bool headphonesFromInputDev = !strcmp(value, "1");
michael@0 334
michael@0 335 if (!headphonesFromInputDev) {
michael@0 336 mHandler.AppendElement(new SwitchHandlerHeadphone(SWITCH_HEADSET_DEVPATH));
michael@0 337 } else {
michael@0 338 // If headphone status will be notified from input dev then initialize
michael@0 339 // status to "off" and wait for event notification.
michael@0 340 mEventInfo[SWITCH_HEADPHONES].mEvent.device() = SWITCH_HEADPHONES;
michael@0 341 mEventInfo[SWITCH_HEADPHONES].mEvent.status() = SWITCH_STATE_OFF;
michael@0 342 }
michael@0 343 mHandler.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB, SWITCH_USB));
michael@0 344 mHandler.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS));
michael@0 345
michael@0 346 SwitchHandlerArray::index_type handlerIndex;
michael@0 347 SwitchHandlerArray::size_type numHandlers = mHandler.Length();
michael@0 348
michael@0 349 for (handlerIndex = 0; handlerIndex < numHandlers; handlerIndex++) {
michael@0 350 SwitchState state = mHandler[handlerIndex]->GetState();
michael@0 351 if (state == SWITCH_STATE_UNKNOWN) {
michael@0 352 continue;
michael@0 353 }
michael@0 354
michael@0 355 SwitchDevice device = mHandler[handlerIndex]->GetType();
michael@0 356 mEventInfo[device].mEvent.device() = device;
michael@0 357 mEventInfo[device].mEvent.status() = state;
michael@0 358 }
michael@0 359 }
michael@0 360
michael@0 361 SwitchDevice GetEventInfo(const NetlinkEvent& aEvent, SwitchState& aState)
michael@0 362 {
michael@0 363 //working around the android code not being const-correct
michael@0 364 NetlinkEvent *e = const_cast<NetlinkEvent*>(&aEvent);
michael@0 365
michael@0 366 for (size_t i = 0; i < mHandler.Length(); i++) {
michael@0 367 if (mHandler[i]->CheckEvent(e)) {
michael@0 368 aState = mHandler[i]->GetState();
michael@0 369 return mHandler[i]->GetType();
michael@0 370 }
michael@0 371 }
michael@0 372 return SWITCH_DEVICE_UNKNOWN;
michael@0 373 }
michael@0 374 };
michael@0 375
michael@0 376 static RefPtr<SwitchEventObserver> sSwitchObserver;
michael@0 377
michael@0 378 static void
michael@0 379 InitializeResourceIfNeed()
michael@0 380 {
michael@0 381 if (!sSwitchObserver) {
michael@0 382 sSwitchObserver = new SwitchEventObserver();
michael@0 383 RegisterUeventListener(sSwitchObserver);
michael@0 384 }
michael@0 385 }
michael@0 386
michael@0 387 static void
michael@0 388 ReleaseResourceIfNeed()
michael@0 389 {
michael@0 390 if (sSwitchObserver->GetEnableCount() == 0) {
michael@0 391 UnregisterUeventListener(sSwitchObserver);
michael@0 392 sSwitchObserver = nullptr;
michael@0 393 }
michael@0 394 }
michael@0 395
michael@0 396 static void
michael@0 397 EnableSwitchNotificationsIOThread(SwitchDevice aDevice, Monitor *aMonitor)
michael@0 398 {
michael@0 399 InitializeResourceIfNeed();
michael@0 400 sSwitchObserver->EnableSwitch(aDevice);
michael@0 401 {
michael@0 402 MonitorAutoLock lock(*aMonitor);
michael@0 403 lock.Notify();
michael@0 404 }
michael@0 405
michael@0 406 // Notify the latest state if IO thread has the information.
michael@0 407 if (sSwitchObserver->GetEnableCount() > 1) {
michael@0 408 sSwitchObserver->NotifyAnEvent(aDevice);
michael@0 409 }
michael@0 410 }
michael@0 411
michael@0 412 void
michael@0 413 EnableSwitchNotifications(SwitchDevice aDevice)
michael@0 414 {
michael@0 415 Monitor monitor("EnableSwitch.monitor");
michael@0 416 {
michael@0 417 MonitorAutoLock lock(monitor);
michael@0 418 XRE_GetIOMessageLoop()->PostTask(
michael@0 419 FROM_HERE,
michael@0 420 NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice, &monitor));
michael@0 421 lock.Wait();
michael@0 422 }
michael@0 423 }
michael@0 424
michael@0 425 static void
michael@0 426 DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
michael@0 427 {
michael@0 428 MOZ_ASSERT(sSwitchObserver->GetEnableCount());
michael@0 429 sSwitchObserver->DisableSwitch(aDevice);
michael@0 430 ReleaseResourceIfNeed();
michael@0 431 }
michael@0 432
michael@0 433 void
michael@0 434 DisableSwitchNotifications(SwitchDevice aDevice)
michael@0 435 {
michael@0 436 XRE_GetIOMessageLoop()->PostTask(
michael@0 437 FROM_HERE,
michael@0 438 NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
michael@0 439 }
michael@0 440
michael@0 441 SwitchState
michael@0 442 GetCurrentSwitchState(SwitchDevice aDevice)
michael@0 443 {
michael@0 444 MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
michael@0 445 return sSwitchObserver->GetCurrentInformation(aDevice);
michael@0 446 }
michael@0 447
michael@0 448 static void
michael@0 449 NotifySwitchStateIOThread(SwitchDevice aDevice, SwitchState aState)
michael@0 450 {
michael@0 451 sSwitchObserver->Notify(aDevice, aState);
michael@0 452 }
michael@0 453
michael@0 454 void NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
michael@0 455 {
michael@0 456 InitializeResourceIfNeed();
michael@0 457 XRE_GetIOMessageLoop()->PostTask(
michael@0 458 FROM_HERE,
michael@0 459 NewRunnableFunction(NotifySwitchStateIOThread, aDevice, aState));
michael@0 460 }
michael@0 461 } // hal_impl
michael@0 462 } //mozilla

mercurial