Bug 736939 - Audio routing support. r=cjones, a=b2g-only
authorSteven Lee <slee@mozilla.com>
Sun, 22 Apr 2012 14:09:22 -0400
changeset 92100 e2af0b79da44efb6b6256109944974889e83d3f0
parent 92099 6c8fe1624a6bd82758e6e38344ddedd35bd9d376
child 92101 ce9fff905894b2d253c4d9cb496e286f6f801d6a
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerscjones, b2g-only
bugs736939
milestone14.0a1
Bug 736939 - Audio routing support. r=cjones, a=b2g-only
hal/Hal.cpp
hal/Hal.h
hal/HalInternal.h
hal/HalTypes.h
hal/Makefile.in
hal/fallback/FallbackSwitch.cpp
hal/gonk/GonkSwitch.cpp
hal/sandbox/PHal.ipdl
hal/sandbox/SandboxHal.cpp
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -591,10 +591,88 @@ LockScreenOrientation(const dom::ScreenO
 
 void
 UnlockScreenOrientation()
 {
   AssertMainThread();
   PROXY_IF_SANDBOXED(UnlockScreenOrientation());
 }
 
+void
+EnableSwitchNotifications(hal::SwitchDevice aDevice) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(EnableSwitchNotifications(aDevice));
+}
+
+void
+DisableSwitchNotifications(hal::SwitchDevice aDevice) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(DisableSwitchNotifications(aDevice));
+}
+
+hal::SwitchState GetCurrentSwitchState(hal::SwitchDevice aDevice)
+{
+  AssertMainThread();
+  RETURN_PROXY_IF_SANDBOXED(GetCurrentSwitchState(aDevice));
+}
+
+typedef mozilla::ObserverList<SwitchEvent> SwitchObserverList;
+
+static SwitchObserverList *sSwitchObserverLists = NULL;
+
+static SwitchObserverList&
+GetSwitchObserverList(hal::SwitchDevice aDevice) {
+  MOZ_ASSERT(0 <= aDevice && aDevice < NUM_SWITCH_DEVICE); 
+  if (sSwitchObserverLists == NULL) {
+    sSwitchObserverLists = new SwitchObserverList[NUM_SWITCH_DEVICE];
+  }
+  return sSwitchObserverLists[aDevice];
+}
+
+static void
+ReleaseObserversIfNeeded() {
+  for (int i = 0; i < NUM_SWITCH_DEVICE; i++) {
+    if (sSwitchObserverLists[i].Length() != 0)
+      return;
+  }
+
+  //The length of every list is 0, no observer in the list.
+  delete [] sSwitchObserverLists;
+  sSwitchObserverLists = NULL;
+}
+
+void
+RegisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aObserver)
+{
+  AssertMainThread();
+  SwitchObserverList& observer = GetSwitchObserverList(aDevice);
+  observer.AddObserver(aObserver);
+  if (observer.Length() == 1) {
+    EnableSwitchNotifications(aDevice);
+  }
+}
+
+void
+UnregisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aObserver)
+{
+  AssertMainThread();
+  SwitchObserverList& observer = GetSwitchObserverList(aDevice);
+  observer.RemoveObserver(aObserver);
+  if (observer.Length() == 0) {
+    DisableSwitchNotifications(aDevice);
+    ReleaseObserversIfNeeded();
+  }
+}
+
+void
+NotifySwitchChange(const hal::SwitchEvent& aEvent)
+{
+  // When callback this notification, main thread may call unregister function
+  // first. We should check if this pointer is valid.
+  if (!sSwitchObserverLists)
+    return;
+
+  SwitchObserverList& observer = GetSwitchObserverList(aEvent.device());
+  observer.Broadcast(aEvent);
+}
+
 } // namespace hal
 } // namespace mozilla
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -340,16 +340,41 @@ void NotifyScreenOrientationChange(const
  */
 bool LockScreenOrientation(const dom::ScreenOrientation& aOrientation);
 
 /**
  * Unlock the screen orientation.
  */
 void UnlockScreenOrientation();
 
+/**
+ * Register an observer for the switch of given SwitchDevice.
+ *
+ * The observer will receive data whenever the data generated by the
+ * given switch.
+ */
+void RegisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aSwitchObserver);
+
+/**
+ * Unregister an observer for the switch of given SwitchDevice.
+ */
+void UnregisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aSwitchObserver);
+
+/**
+ * Notify the state of the switch. 
+ *
+ * This API is internal to hal; clients shouldn't call it directly.
+ */
+void NotifySwitchChange(const hal::SwitchEvent& aEvent);
+
+/**
+ * Get current switch information.
+ */
+hal::SwitchState GetCurrentSwitchState(hal::SwitchDevice aDevice);
+
 } // namespace MOZ_HAL_NAMESPACE
 } // namespace mozilla
 
 #ifdef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_HAL_NAMESPACE
 #endif
 
--- a/hal/HalInternal.h
+++ b/hal/HalInternal.h
@@ -82,12 +82,22 @@ void DisableNetworkNotifications();
  */
 void EnableScreenOrientationNotifications();
 
 /**
  * Disables screen orientation notifications from the backend.
  */
 void DisableScreenOrientationNotifications();
 
+/**
+ * Enable switch notifications from the backend
+ */
+void EnableSwitchNotifications(hal::SwitchDevice aDevice);
+
+/**
+ * Disable switch notifications from the backend
+ */
+void DisableSwitchNotifications(hal::SwitchDevice aDevice);
+
 } // namespace MOZ_HAL_NAMESPACE
 } // namespace mozilla
 
 #endif  // mozilla_HalInternal_h
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -32,16 +32,32 @@ enum LightMode {
     eHalLightMode_Sensor = 1      // brightness is managed by a light sensor
 };
 enum FlashMode {
     eHalLightFlash_None = 0,
     eHalLightFlash_Timed = 1,     // timed flashing.  Use flashOnMS and flashOffMS for timing
     eHalLightFlash_Hardware = 2   // hardware assisted flashing
 };
 
+class SwitchEvent;
+
+enum SwitchDevice {
+  SWITCH_DEVICE_UNKNOWN = -1,
+  SWITCH_HEADPHONES,
+  NUM_SWITCH_DEVICE
+};
+
+enum SwitchState {
+  SWITCH_STATE_UNKNOWN = -1,
+  SWITCH_STATE_ON,
+  SWITCH_STATE_OFF,
+  NUM_SWITCH_STATE
+};
+
+typedef Observer<SwitchEvent> SwitchObserver;
 } // namespace hal
 } // namespace mozilla
 
 namespace mozilla {
 namespace hal {
 
 /**
  * Used by ModifyWakeLock
@@ -92,11 +108,32 @@ struct ParamTraits<mozilla::hal::FlashMo
  */
 template <>
 struct ParamTraits<mozilla::hal::WakeLockControl>
   : public EnumSerializer<mozilla::hal::WakeLockControl,
                           mozilla::hal::WAKE_LOCK_REMOVE_ONE,
                           mozilla::hal::WAKE_LOCK_ADD_ONE>
 {};
 
+/**
+ * Serializer for SwitchState
+ */
+template <>
+struct ParamTraits<mozilla::hal::SwitchState>:
+  public EnumSerializer<mozilla::hal::SwitchState,
+                        mozilla::hal::SWITCH_STATE_UNKNOWN,
+                        mozilla::hal::NUM_SWITCH_STATE> {
+};
+
+/**
+ * Serializer for SwitchDevice
+ */
+template <>
+struct ParamTraits<mozilla::hal::SwitchDevice>:
+  public EnumSerializer<mozilla::hal::SwitchDevice,
+                        mozilla::hal::SWITCH_DEVICE_UNKNOWN,
+                        mozilla::hal::NUM_SWITCH_DEVICE> {
+};
+
+
 } // namespace IPC
 
 #endif // mozilla_hal_Types_h
--- a/hal/Makefile.in
+++ b/hal/Makefile.in
@@ -80,16 +80,17 @@ CPPSRCS += \
   AndroidSensor.cpp \
   $(NULL)
 else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += \
   GonkHal.cpp \
   Power.cpp \
   GonkSensor.cpp \
   UeventPoller.cpp \
+  GonkSwitch.cpp \
   $(NULL)
 else ifeq (Linux,$(OS_TARGET))
 CPPSRCS += \
   LinuxHal.cpp \
   FallbackSensor.cpp \
   Power.cpp \
   $(NULL)
 ifdef MOZ_ENABLE_DBUS
@@ -116,16 +117,17 @@ CPPSRCS += \
   $(NULL)
 endif
 
 ifneq (gonk,$(MOZ_WIDGET_TOOLKIT)) #{
 CPPSRCS += \
   FallbackLights.cpp  \
   FallbackTime.cpp \
   FallbackWakeLocks.cpp \
+  FallbackSwitch.cpp \
   $(NULL)
 endif #}
 
 # Screen Orientation backend
 ifneq (android,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += ScreenOrientationFallback.cpp
 endif
 
new file mode 100644
--- /dev/null
+++ b/hal/fallback/FallbackSwitch.cpp
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Hal.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+EnableSwitchNotifications(SwitchDevice aDevice)
+{
+}
+
+void
+DisableSwitchNotifications(SwitchDevice aDevice)
+{
+}
+
+SwitchState
+GetCurrentSwitchState(SwitchDevice aDevice) {
+  return SWITCH_STATE_UNKNOWN;
+}
+
+} // namespace hal_impl
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/hal/gonk/GonkSwitch.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <android/log.h>
+#include <sysutils/NetlinkEvent.h>
+
+#include "base/message_loop.h"
+
+#include "Hal.h"
+#include "nsXULAppAPI.h"
+#include "UeventPoller.h"
+
+using namespace mozilla::hal;
+
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GonkSwitch" , ## args) 
+
+namespace mozilla {
+namespace hal_impl {
+
+struct {const char* name; SwitchDevice device; } kSwitchNameMap[] = {
+  { "h2w", SWITCH_HEADPHONES },
+  { NULL, SWITCH_DEVICE_UNKNOWN },
+};
+
+static SwitchDevice
+NameToDevice(const char* name) {
+  for (int i = 0; kSwitchNameMap[i].device != SWITCH_DEVICE_UNKNOWN; i++) {
+    if (strcmp(name, kSwitchNameMap[i].name) == 0) {
+      return kSwitchNameMap[i].device;
+    }
+  }
+  return SWITCH_DEVICE_UNKNOWN;
+}
+
+class SwitchEventRunnable : public nsRunnable
+{
+public:
+  SwitchEventRunnable(SwitchEvent& event) : mEvent(event) {}
+
+  NS_IMETHOD Run() {
+    NotifySwitchChange(mEvent);
+    return NS_OK;
+  }
+private:
+  SwitchEvent mEvent;
+};
+
+class SwitchEventObserver : public IUeventObserver
+{
+public:
+  SwitchEventObserver() : mEnableNum(0) {
+   InternalInit();
+  }
+  ~SwitchEventObserver() {}
+
+  int GetEnableCount() {
+    return mEnableNum;
+  }
+
+  void EnableSwitch(SwitchDevice aDevice) {
+    mEventInfo[aDevice].mEnable = true;
+    mEnableNum++;
+  }
+
+  void DisableSwitch(SwitchDevice aDevice) {
+    mEventInfo[aDevice].mEnable = false;
+    mEnableNum--;
+  }
+
+  void Notify(const NetlinkEvent& event) {
+    const char* name;
+    const char* state;
+   
+    SwitchDevice device = ProcessEvent(event, &name, &state);
+    if (device == SWITCH_DEVICE_UNKNOWN) { 
+      return; 
+    } 
+
+    EventInfo& info = mEventInfo[device]; 
+    info.mEvent.status() = atoi(state) == 0 ? SWITCH_STATE_OFF : SWITCH_STATE_ON; 
+    if (info.mEnable) { 
+      NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent)); 
+    } 
+  }
+
+  SwitchState GetCurrentInformation(SwitchDevice aDevice) {
+    return mEventInfo[aDevice].mEvent.status();
+  }
+
+private:
+  class EventInfo {
+  public:
+    EventInfo() : mEnable(false) {}
+    SwitchEvent mEvent;
+    bool mEnable;
+  };
+
+  EventInfo mEventInfo[NUM_SWITCH_DEVICE];
+  size_t mEnableNum;
+
+  void InternalInit() {
+    for (int i = 0; i < NUM_SWITCH_DEVICE; i++) {
+      mEventInfo[i].mEvent.device() = kSwitchNameMap[i].device;
+      mEventInfo[i].mEvent.status() = SWITCH_STATE_UNKNOWN;
+    }
+  }
+
+  bool GetEventInfo(const NetlinkEvent& event, const char** name, const char** state) {
+    //working around the android code not being const-correct
+    NetlinkEvent *e = const_cast<NetlinkEvent*>(&event);
+    const char* subsystem = e->getSubsystem();
+ 
+    if (!subsystem || strcmp(subsystem, "switch")) {
+      return false;
+    }
+
+    *name = e->findParam("SWITCH_NAME");
+    *state = e->findParam("SWITCH_STATE");
+
+    if (!*name || !*state) {
+      return false;
+    }
+    return true;
+  }
+
+  SwitchDevice ProcessEvent(const NetlinkEvent& event, const char** name, const char** state) {
+    bool rv = GetEventInfo(event, name, state);
+    NS_ENSURE_TRUE(rv, SWITCH_DEVICE_UNKNOWN);
+    return NameToDevice(*name);
+  }
+};
+
+SwitchEventObserver* sSwitchObserver;
+
+static void
+InitializeResourceIfNeed()
+{
+  if (!sSwitchObserver) {
+    sSwitchObserver = new SwitchEventObserver();
+    RegisterUeventListener(sSwitchObserver);
+  }
+}
+
+static void
+ReleaseResourceIfNeed()
+{
+  if (sSwitchObserver->GetEnableCount() == 0) {
+    UnregisterUeventListener(sSwitchObserver);
+    delete sSwitchObserver;
+    sSwitchObserver = NULL;
+  }
+}
+
+static void
+EnableSwitchNotificationsIOThread(SwitchDevice aDevice)
+{
+  InitializeResourceIfNeed();
+  sSwitchObserver->EnableSwitch(aDevice);
+}
+
+void
+EnableSwitchNotifications(SwitchDevice aDevice)
+{
+  XRE_GetIOMessageLoop()->PostTask(
+      FROM_HERE,
+      NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice));
+}
+
+static void
+DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
+{
+  MOZ_ASSERT(sSwitchObserver->GetEnableCount());
+  sSwitchObserver->DisableSwitch(aDevice);
+  ReleaseResourceIfNeed();
+}
+
+void
+DisableSwitchNotifications(SwitchDevice aDevice)
+{
+  XRE_GetIOMessageLoop()->PostTask(
+      FROM_HERE,
+      NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
+}
+
+SwitchState
+GetCurrentSwitchState(SwitchDevice aDevice)
+{
+  MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
+  return sSwitchObserver->GetCurrentInformation(aDevice);
+}
+
+} // hal_impl
+} //mozilla
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -47,16 +47,18 @@ include "mozilla/dom/ScreenOrientation.h
 using PRTime;
 using mozilla::hal::FlashMode;
 using mozilla::hal::LightType;
 using mozilla::hal::LightMode;
 using mozilla::hal::SensorType;
 using mozilla::hal::SensorAccuracyType;
 using mozilla::hal::WakeLockControl;
 using mozilla::dom::ScreenOrientation;
+using mozilla::hal::SwitchState;
+using mozilla::hal::SwitchDevice;
 
 namespace mozilla {
 
 namespace hal {
   struct BatteryInformation {
     double level;
     bool   charging;
     double remainingTime;
@@ -77,16 +79,21 @@ namespace hal {
     float[] values;
     SensorAccuracyType accuracy;
   };
 
   struct NetworkInformation {
     double bandwidth;
     bool   canBeMetered;
   };
+
+  struct SwitchEvent {
+    SwitchDevice device;
+    SwitchState status;
+  };
 }
 
 namespace hal {
   struct WakeLockInformation {
     uint32_t numLocks;
     uint32_t numHidden;
     nsString topic;
   };
@@ -97,16 +104,17 @@ namespace hal_sandbox {
 sync protocol PHal {
     manager PContent;
 
 child:
     NotifyBatteryChange(BatteryInformation aBatteryInfo);
     NotifyNetworkChange(NetworkInformation aNetworkInfo);
     NotifyWakeLockChange(WakeLockInformation aWakeLockInfo);
     NotifyScreenOrientationChange(ScreenOrientation aScreenOrientation);
+    NotifySwitchChange(SwitchEvent aEvent);
 
 parent:
     Vibrate(uint32[] pattern, uint64[] id, PBrowser browser);
     CancelVibrate(uint64[] id, PBrowser browser);
 
     EnableBatteryNotifications();
     DisableBatteryNotifications();
     sync GetCurrentBatteryInformation()
@@ -145,16 +153,21 @@ parent:
 
     EnableScreenOrientationNotifications();
     DisableScreenOrientationNotifications();
     sync GetCurrentScreenOrientation()
       returns (ScreenOrientation aScreenOrientation);
     sync LockScreenOrientation(ScreenOrientation aOrientation)
       returns (bool allowed);
     UnlockScreenOrientation();
+ 
+    EnableSwitchNotifications(SwitchDevice aDevice);
+    DisableSwitchNotifications(SwitchDevice aDevice);
+    sync GetCurrentSwitchState(SwitchDevice aDevice)
+      returns (SwitchState aState);
 
 child:
     NotifySensorChange(SensorData aSensorData);
 
 parent:    
     EnableSensorNotifications(SensorType aSensor);
     DisableSensorNotifications(SensorType aSensor);
 
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -235,22 +235,43 @@ ModifyWakeLock(const nsAString &aTopic, 
 }
 
 void
 GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
 {
   Hal()->SendGetWakeLockInfo(nsString(aTopic), aWakeLockInfo);
 }
 
+void
+EnableSwitchNotifications(SwitchDevice aDevice)
+{
+  Hal()->SendEnableSwitchNotifications(aDevice);
+}
+
+void
+DisableSwitchNotifications(SwitchDevice aDevice)
+{
+  Hal()->SendDisableSwitchNotifications(aDevice);
+}
+
+SwitchState
+GetCurrentSwitchState(SwitchDevice aDevice)
+{
+  SwitchState state;
+  Hal()->SendGetCurrentSwitchState(aDevice, &state);
+  return state;
+}
+
 class HalParent : public PHalParent
                 , public BatteryObserver
                 , public NetworkObserver
                 , public ISensorObserver
                 , public WakeLockObserver
                 , public ScreenOrientationObserver
+                , public SwitchObserver
 {
 public:
   NS_OVERRIDE virtual bool
   RecvVibrate(const InfallibleTArray<unsigned int>& pattern,
               const InfallibleTArray<uint64> &id,
               PBrowserParent *browserParent)
   {
     // Check whether browserParent is active.  We should have already
@@ -496,16 +517,42 @@ public:
     hal::GetWakeLockInfo(aTopic, aWakeLockInfo);
     return true;
   }
   
   void Notify(const WakeLockInformation& aWakeLockInfo)
   {
     unused << SendNotifyWakeLockChange(aWakeLockInfo);
   }
+
+  NS_OVERRIDE virtual bool
+  RecvEnableSwitchNotifications(const SwitchDevice& aDevice) 
+  {
+    hal::RegisterSwitchObserver(aDevice, this);
+    return true;
+  }
+
+  NS_OVERRIDE virtual bool
+  RecvDisableSwitchNotifications(const SwitchDevice& aDevice) 
+  {
+    hal::UnregisterSwitchObserver(aDevice, this);
+    return true;
+  }
+
+  void Notify(const SwitchEvent& aSwitchEvent)
+  {
+    unused << SendNotifySwitchChange(aSwitchEvent);
+  }
+
+  NS_OVERRIDE virtual bool
+  RecvGetCurrentSwitchState(const SwitchDevice& aDevice, hal::SwitchState *aState)
+  {
+    *aState = hal::GetCurrentSwitchState(aDevice);
+    return true;
+  }
 };
 
 class HalChild : public PHalChild {
 public:
   NS_OVERRIDE virtual bool
   RecvNotifyBatteryChange(const BatteryInformation& aBatteryInfo) {
     hal::NotifyBatteryChange(aBatteryInfo);
     return true;
@@ -526,16 +573,22 @@ public:
     return true;
   }
 
   NS_OVERRIDE virtual bool
   RecvNotifyScreenOrientationChange(const ScreenOrientation& aScreenOrientation) {
     hal::NotifyScreenOrientationChange(aScreenOrientation);
     return true;
   }
+
+  NS_OVERRIDE virtual bool
+  RecvNotifySwitchChange(const mozilla::hal::SwitchEvent& aEvent) {
+    hal::NotifySwitchChange(aEvent);
+    return true;
+  }
 };
 
 bool
 HalChild::RecvNotifySensorChange(const hal::SensorData &aSensorData) {
   hal::NotifySensorChange(aSensorData);
   
   return true;
 }