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

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

mercurial