forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			452 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			452 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set sw=2 ts=8 et ft=cpp : */
 | |
| /* 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 "Hal.h"
 | |
| 
 | |
| #include "HalImpl.h"
 | |
| #include "HalLog.h"
 | |
| #include "HalSandbox.h"
 | |
| #include "HalWakeLockInternal.h"
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include "nsXULAppAPI.h"
 | |
| #include "nsPIDOMWindow.h"
 | |
| #include "mozilla/Observer.h"
 | |
| #include "mozilla/dom/ContentChild.h"
 | |
| #include "WindowIdentifier.h"
 | |
| 
 | |
| #ifdef XP_WIN
 | |
| #  include <process.h>
 | |
| #  define getpid _getpid
 | |
| #endif
 | |
| 
 | |
| using namespace mozilla::services;
 | |
| using namespace mozilla::dom;
 | |
| 
 | |
| #define PROXY_IF_SANDBOXED(_call)              \
 | |
|   do {                                         \
 | |
|     if (InSandbox()) {                         \
 | |
|       if (!hal_sandbox::HalChildDestroyed()) { \
 | |
|         hal_sandbox::_call;                    \
 | |
|       }                                        \
 | |
|     } else {                                   \
 | |
|       hal_impl::_call;                         \
 | |
|     }                                          \
 | |
|   } while (0)
 | |
| 
 | |
| #define RETURN_PROXY_IF_SANDBOXED(_call, defValue) \
 | |
|   do {                                             \
 | |
|     if (InSandbox()) {                             \
 | |
|       if (hal_sandbox::HalChildDestroyed()) {      \
 | |
|         return defValue;                           \
 | |
|       }                                            \
 | |
|       return hal_sandbox::_call;                   \
 | |
|     } else {                                       \
 | |
|       return hal_impl::_call;                      \
 | |
|     }                                              \
 | |
|   } while (0)
 | |
| 
 | |
| namespace mozilla::hal {
 | |
| 
 | |
| static bool sInitialized = false;
 | |
| 
 | |
| mozilla::LogModule* GetHalLog() {
 | |
|   static mozilla::LazyLogModule sHalLog("hal");
 | |
|   return sHalLog;
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| void AssertMainThread() { MOZ_ASSERT(NS_IsMainThread()); }
 | |
| 
 | |
| bool InSandbox() { return GeckoProcessType_Content == XRE_GetProcessType(); }
 | |
| 
 | |
| bool WindowIsActive(nsPIDOMWindowInner* aWindow) {
 | |
|   NS_ENSURE_TRUE(aWindow, false);
 | |
|   dom::Document* document = aWindow->GetDoc();
 | |
|   NS_ENSURE_TRUE(document, false);
 | |
|   return !document->Hidden();
 | |
| }
 | |
| 
 | |
| StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
 | |
| 
 | |
| static void RecordLastIDToVibrate(const WindowIdentifier& aId) {
 | |
|   if (!InSandbox()) {
 | |
|     *gLastIDToVibrate = aId.AsArray().Clone();
 | |
|   }
 | |
| }
 | |
| 
 | |
| static bool MayCancelVibration(const WindowIdentifier& aId) {
 | |
|   // Although only active windows may start vibrations, a window may
 | |
|   // cancel its own vibration even if it's no longer active.
 | |
|   //
 | |
|   // After a window is marked as inactive, it sends a CancelVibrate
 | |
|   // request.  We want this request to cancel a playing vibration
 | |
|   // started by that window, so we certainly don't want to reject the
 | |
|   // cancellation request because the window is now inactive.
 | |
|   //
 | |
|   // But it could be the case that, after this window became inactive,
 | |
|   // some other window came along and started a vibration.  We don't
 | |
|   // want this window's cancellation request to cancel that window's
 | |
|   // actively-playing vibration!
 | |
|   //
 | |
|   // To solve this problem, we keep track of the id of the last window
 | |
|   // to start a vibration, and only accepts cancellation requests from
 | |
|   // the same window.  All other cancellation requests are ignored.
 | |
| 
 | |
|   return InSandbox() || (*gLastIDToVibrate == aId.AsArray());
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| void Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window) {
 | |
|   Vibrate(pattern, WindowIdentifier(window));
 | |
| }
 | |
| 
 | |
| void Vibrate(const nsTArray<uint32_t>& pattern, WindowIdentifier&& id) {
 | |
|   AssertMainThread();
 | |
| 
 | |
|   // Only active windows may start vibrations.  If |id| hasn't gone
 | |
|   // through the IPC layer -- that is, if our caller is the outside
 | |
|   // world, not hal_proxy -- check whether the window is active.  If
 | |
|   // |id| has gone through IPC, don't check the window's visibility;
 | |
|   // only the window corresponding to the bottommost process has its
 | |
|   // visibility state set correctly.
 | |
|   if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
 | |
|     HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RecordLastIDToVibrate(id);
 | |
| 
 | |
|   // Don't forward our ID if we are not in the sandbox, because hal_impl
 | |
|   // doesn't need it, and we don't want it to be tempted to read it.  The
 | |
|   // empty identifier will assert if it's used.
 | |
|   PROXY_IF_SANDBOXED(
 | |
|       Vibrate(pattern, InSandbox() ? std::move(id) : WindowIdentifier()));
 | |
| }
 | |
| 
 | |
| void CancelVibrate(nsPIDOMWindowInner* window) {
 | |
|   CancelVibrate(WindowIdentifier(window));
 | |
| }
 | |
| 
 | |
| void CancelVibrate(WindowIdentifier&& id) {
 | |
|   AssertMainThread();
 | |
| 
 | |
|   if (MayCancelVibration(id)) {
 | |
|     // Don't forward our ID if we are not in the sandbox, because hal_impl
 | |
|     // doesn't need it, and we don't want it to be tempted to read it.  The
 | |
|     // empty identifier will assert if it's used.
 | |
|     PROXY_IF_SANDBOXED(
 | |
|         CancelVibrate(InSandbox() ? std::move(id) : WindowIdentifier()));
 | |
|   }
 | |
| }
 | |
| 
 | |
| template <class InfoType>
 | |
| class ObserversManager {
 | |
|  public:
 | |
|   void AddObserver(Observer<InfoType>* aObserver) {
 | |
|     mObservers.AddObserver(aObserver);
 | |
| 
 | |
|     if (mObservers.Length() == 1) {
 | |
|       EnableNotifications();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void RemoveObserver(Observer<InfoType>* aObserver) {
 | |
|     bool removed = mObservers.RemoveObserver(aObserver);
 | |
|     if (!removed) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (mObservers.Length() == 0) {
 | |
|       DisableNotifications();
 | |
|       OnNotificationsDisabled();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void BroadcastInformation(const InfoType& aInfo) {
 | |
|     mObservers.Broadcast(aInfo);
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   ~ObserversManager() { MOZ_ASSERT(mObservers.Length() == 0); }
 | |
| 
 | |
|   virtual void EnableNotifications() = 0;
 | |
|   virtual void DisableNotifications() = 0;
 | |
|   virtual void OnNotificationsDisabled() {}
 | |
| 
 | |
|  private:
 | |
|   mozilla::ObserverList<InfoType> mObservers;
 | |
| };
 | |
| 
 | |
| template <class InfoType>
 | |
| class CachingObserversManager : public ObserversManager<InfoType> {
 | |
|  public:
 | |
|   InfoType GetCurrentInformation() {
 | |
|     if (mHasValidCache) {
 | |
|       return mInfo;
 | |
|     }
 | |
| 
 | |
|     GetCurrentInformationInternal(&mInfo);
 | |
|     mHasValidCache = true;
 | |
|     return mInfo;
 | |
|   }
 | |
| 
 | |
|   void CacheInformation(const InfoType& aInfo) {
 | |
|     mHasValidCache = true;
 | |
|     mInfo = aInfo;
 | |
|   }
 | |
| 
 | |
|   void BroadcastCachedInformation() { this->BroadcastInformation(mInfo); }
 | |
| 
 | |
|  protected:
 | |
|   virtual void GetCurrentInformationInternal(InfoType*) = 0;
 | |
| 
 | |
|   void OnNotificationsDisabled() override { mHasValidCache = false; }
 | |
| 
 | |
|  private:
 | |
|   InfoType mInfo;
 | |
|   bool mHasValidCache;
 | |
| };
 | |
| 
 | |
| class BatteryObserversManager final
 | |
|     : public CachingObserversManager<BatteryInformation> {
 | |
|  protected:
 | |
|   void EnableNotifications() override {
 | |
|     PROXY_IF_SANDBOXED(EnableBatteryNotifications());
 | |
|   }
 | |
| 
 | |
|   void DisableNotifications() override {
 | |
|     PROXY_IF_SANDBOXED(DisableBatteryNotifications());
 | |
|   }
 | |
| 
 | |
|   void GetCurrentInformationInternal(BatteryInformation* aInfo) override {
 | |
|     PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo));
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NetworkObserversManager final
 | |
|     : public CachingObserversManager<NetworkInformation> {
 | |
|  protected:
 | |
|   void EnableNotifications() override {
 | |
|     PROXY_IF_SANDBOXED(EnableNetworkNotifications());
 | |
|   }
 | |
| 
 | |
|   void DisableNotifications() override {
 | |
|     PROXY_IF_SANDBOXED(DisableNetworkNotifications());
 | |
|   }
 | |
| 
 | |
|   void GetCurrentInformationInternal(NetworkInformation* aInfo) override {
 | |
|     PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo));
 | |
|   }
 | |
| };
 | |
| 
 | |
| class WakeLockObserversManager final
 | |
|     : public ObserversManager<WakeLockInformation> {
 | |
|  protected:
 | |
|   void EnableNotifications() override {
 | |
|     PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
 | |
|   }
 | |
| 
 | |
|   void DisableNotifications() override {
 | |
|     PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
 | |
|   }
 | |
| };
 | |
| 
 | |
| typedef mozilla::ObserverList<SensorData> SensorObserverList;
 | |
| StaticAutoPtr<SensorObserverList> sSensorObservers[NUM_SENSOR_TYPE];
 | |
| 
 | |
| static SensorObserverList* GetSensorObservers(SensorType sensor_type) {
 | |
|   AssertMainThread();
 | |
|   MOZ_ASSERT(sensor_type < NUM_SENSOR_TYPE);
 | |
| 
 | |
|   if (!sSensorObservers[sensor_type]) {
 | |
|     sSensorObservers[sensor_type] = new SensorObserverList();
 | |
|   }
 | |
| 
 | |
|   return sSensorObservers[sensor_type];
 | |
| }
 | |
| 
 | |
| #define MOZ_IMPL_HAL_OBSERVER(name_)                             \
 | |
|   StaticAutoPtr<name_##ObserversManager> s##name_##Observers;    \
 | |
|                                                                  \
 | |
|   static name_##ObserversManager* name_##Observers() {           \
 | |
|     AssertMainThread();                                          \
 | |
|                                                                  \
 | |
|     if (!s##name_##Observers) {                                  \
 | |
|       MOZ_ASSERT(sInitialized);                                  \
 | |
|       s##name_##Observers = new name_##ObserversManager();       \
 | |
|     }                                                            \
 | |
|                                                                  \
 | |
|     return s##name_##Observers;                                  \
 | |
|   }                                                              \
 | |
|                                                                  \
 | |
|   void Register##name_##Observer(name_##Observer* aObserver) {   \
 | |
|     AssertMainThread();                                          \
 | |
|     name_##Observers()->AddObserver(aObserver);                  \
 | |
|   }                                                              \
 | |
|                                                                  \
 | |
|   void Unregister##name_##Observer(name_##Observer* aObserver) { \
 | |
|     AssertMainThread();                                          \
 | |
|     name_##Observers()->RemoveObserver(aObserver);               \
 | |
|   }
 | |
| 
 | |
| MOZ_IMPL_HAL_OBSERVER(Battery)
 | |
| 
 | |
| void GetCurrentBatteryInformation(BatteryInformation* aInfo) {
 | |
|   *aInfo = BatteryObservers()->GetCurrentInformation();
 | |
| }
 | |
| 
 | |
| void NotifyBatteryChange(const BatteryInformation& aInfo) {
 | |
|   BatteryObservers()->CacheInformation(aInfo);
 | |
|   BatteryObservers()->BroadcastCachedInformation();
 | |
| }
 | |
| 
 | |
| void EnableSensorNotifications(SensorType aSensor) {
 | |
|   AssertMainThread();
 | |
|   PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor));
 | |
| }
 | |
| 
 | |
| void DisableSensorNotifications(SensorType aSensor) {
 | |
|   AssertMainThread();
 | |
|   PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor));
 | |
| }
 | |
| 
 | |
| void RegisterSensorObserver(SensorType aSensor, ISensorObserver* aObserver) {
 | |
|   SensorObserverList* observers = GetSensorObservers(aSensor);
 | |
| 
 | |
|   observers->AddObserver(aObserver);
 | |
|   if (observers->Length() == 1) {
 | |
|     EnableSensorNotifications(aSensor);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void UnregisterSensorObserver(SensorType aSensor, ISensorObserver* aObserver) {
 | |
|   SensorObserverList* observers = GetSensorObservers(aSensor);
 | |
|   if (!observers->RemoveObserver(aObserver) || observers->Length() > 0) {
 | |
|     return;
 | |
|   }
 | |
|   DisableSensorNotifications(aSensor);
 | |
| }
 | |
| 
 | |
| void NotifySensorChange(const SensorData& aSensorData) {
 | |
|   SensorObserverList* observers = GetSensorObservers(aSensorData.sensor());
 | |
| 
 | |
|   observers->Broadcast(aSensorData);
 | |
| }
 | |
| 
 | |
| MOZ_IMPL_HAL_OBSERVER(Network)
 | |
| 
 | |
| void GetCurrentNetworkInformation(NetworkInformation* aInfo) {
 | |
|   *aInfo = NetworkObservers()->GetCurrentInformation();
 | |
| }
 | |
| 
 | |
| void NotifyNetworkChange(const NetworkInformation& aInfo) {
 | |
|   NetworkObservers()->CacheInformation(aInfo);
 | |
|   NetworkObservers()->BroadcastCachedInformation();
 | |
| }
 | |
| 
 | |
| MOZ_IMPL_HAL_OBSERVER(WakeLock)
 | |
| 
 | |
| void ModifyWakeLock(const nsAString& aTopic, WakeLockControl aLockAdjust,
 | |
|                     WakeLockControl aHiddenAdjust) {
 | |
|   AssertMainThread();
 | |
| 
 | |
|   PROXY_IF_SANDBOXED(ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust));
 | |
| }
 | |
| 
 | |
| void GetWakeLockInfo(const nsAString& aTopic,
 | |
|                      WakeLockInformation* aWakeLockInfo) {
 | |
|   AssertMainThread();
 | |
|   PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo));
 | |
| }
 | |
| 
 | |
| void NotifyWakeLockChange(const WakeLockInformation& aInfo) {
 | |
|   AssertMainThread();
 | |
|   WakeLockObservers()->BroadcastInformation(aInfo);
 | |
| }
 | |
| 
 | |
| RefPtr<GenericNonExclusivePromise> LockScreenOrientation(
 | |
|     const ScreenOrientation& aOrientation) {
 | |
|   AssertMainThread();
 | |
|   RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation), nullptr);
 | |
| }
 | |
| 
 | |
| void UnlockScreenOrientation() {
 | |
|   AssertMainThread();
 | |
|   PROXY_IF_SANDBOXED(UnlockScreenOrientation());
 | |
| }
 | |
| 
 | |
| void SetProcessPriority(int aPid, ProcessPriority aPriority) {
 | |
|   // n.b. The sandboxed implementation crashes; SetProcessPriority works only
 | |
|   // from the main process.
 | |
|   PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority));
 | |
| }
 | |
| 
 | |
| // From HalTypes.h.
 | |
| const char* ProcessPriorityToString(ProcessPriority aPriority) {
 | |
|   switch (aPriority) {
 | |
|     case PROCESS_PRIORITY_PARENT_PROCESS:
 | |
|       return "PARENT_PROCESS";
 | |
|     case PROCESS_PRIORITY_PREALLOC:
 | |
|       return "PREALLOC";
 | |
|     case PROCESS_PRIORITY_FOREGROUND_HIGH:
 | |
|       return "FOREGROUND_HIGH";
 | |
|     case PROCESS_PRIORITY_FOREGROUND:
 | |
|       return "FOREGROUND";
 | |
|     case PROCESS_PRIORITY_FOREGROUND_KEYBOARD:
 | |
|       return "FOREGROUND_KEYBOARD";
 | |
|     case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
 | |
|       return "BACKGROUND_PERCEIVABLE";
 | |
|     case PROCESS_PRIORITY_BACKGROUND:
 | |
|       return "BACKGROUND";
 | |
|     case PROCESS_PRIORITY_UNKNOWN:
 | |
|       return "UNKNOWN";
 | |
|     default:
 | |
|       MOZ_ASSERT(false);
 | |
|       return "???";
 | |
|   }
 | |
| }
 | |
| 
 | |
| UniquePtr<hal::PerformanceHintSession> CreatePerformanceHintSession(
 | |
|     const nsTArray<PlatformThreadHandle>& aThreads,
 | |
|     mozilla::TimeDuration aTargetWorkDuration) {
 | |
|   return hal_impl::CreatePerformanceHintSession(aThreads, aTargetWorkDuration);
 | |
| }
 | |
| 
 | |
| const Maybe<hal::HeterogeneousCpuInfo>& GetHeterogeneousCpuInfo() {
 | |
|   return hal_impl::GetHeterogeneousCpuInfo();
 | |
| }
 | |
| 
 | |
| void Init() {
 | |
|   MOZ_ASSERT(!sInitialized);
 | |
| 
 | |
|   if (!InSandbox()) {
 | |
|     gLastIDToVibrate = new WindowIdentifier::IDArrayType();
 | |
|   }
 | |
| 
 | |
|   WakeLockInit();
 | |
| 
 | |
|   sInitialized = true;
 | |
| }
 | |
| 
 | |
| void Shutdown() {
 | |
|   MOZ_ASSERT(sInitialized);
 | |
| 
 | |
|   gLastIDToVibrate = nullptr;
 | |
| 
 | |
|   sBatteryObservers = nullptr;
 | |
|   sNetworkObservers = nullptr;
 | |
|   sWakeLockObservers = nullptr;
 | |
| 
 | |
|   for (auto& sensorObserver : sSensorObservers) {
 | |
|     sensorObserver = nullptr;
 | |
|   }
 | |
| 
 | |
|   sInitialized = false;
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla::hal
 | 
