Bug 736939 - Audio routing support. r=cjones, a=b2g-only
authorSteven Lee <slee@mozilla.com>
Sun, 22 Apr 2012 14:09:22 -0400
changeset 95842 e2af0b79da44efb6b6256109944974889e83d3f0
parent 95841 6c8fe1624a6bd82758e6e38344ddedd35bd9d376
child 95843 ce9fff905894b2d253c4d9cb496e286f6f801d6a
push id160
push userlsblakk@mozilla.com
push dateFri, 13 Jul 2012 18:18:57 +0000
treeherdermozilla-release@228ba1a111fc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones, b2g-only
bugs736939
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;
 }