Hal.cpp (14140B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et ft=cpp : */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "Hal.h" 8 9 #include "HalImpl.h" 10 #include "HalLog.h" 11 #include "HalSandbox.h" 12 #include "HalWakeLockInternal.h" 13 #include "mozilla/dom/Document.h" 14 #include "nsXULAppAPI.h" 15 #include "nsPIDOMWindow.h" 16 #include "mozilla/Observer.h" 17 #include "mozilla/dom/ContentChild.h" 18 #include "WindowIdentifier.h" 19 20 #ifdef XP_WIN 21 # include <process.h> 22 # define getpid _getpid 23 #endif 24 25 using namespace mozilla::services; 26 using namespace mozilla::dom; 27 28 #define PROXY_IF_SANDBOXED(_call) \ 29 do { \ 30 if (InSandbox()) { \ 31 if (!hal_sandbox::HalChildDestroyed()) { \ 32 hal_sandbox::_call; \ 33 } \ 34 } else { \ 35 hal_impl::_call; \ 36 } \ 37 } while (0) 38 39 #define RETURN_PROXY_IF_SANDBOXED(_call, defValue) \ 40 do { \ 41 if (InSandbox()) { \ 42 if (hal_sandbox::HalChildDestroyed()) { \ 43 return defValue; \ 44 } \ 45 return hal_sandbox::_call; \ 46 } else { \ 47 return hal_impl::_call; \ 48 } \ 49 } while (0) 50 51 namespace mozilla::hal { 52 53 static bool sInitialized = false; 54 55 mozilla::LogModule* GetHalLog() { 56 static mozilla::LazyLogModule sHalLog("hal"); 57 return sHalLog; 58 } 59 60 namespace { 61 62 void AssertMainThread() { MOZ_ASSERT(NS_IsMainThread()); } 63 64 bool InSandbox() { return GeckoProcessType_Content == XRE_GetProcessType(); } 65 66 bool WindowIsActive(nsPIDOMWindowInner* aWindow) { 67 NS_ENSURE_TRUE(aWindow, false); 68 dom::Document* document = aWindow->GetDoc(); 69 NS_ENSURE_TRUE(document, false); 70 return !document->Hidden(); 71 } 72 73 StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate; 74 75 static void RecordLastIDToVibrate(const WindowIdentifier& aId) { 76 if (!InSandbox()) { 77 *gLastIDToVibrate = aId.AsArray().Clone(); 78 } 79 } 80 81 static bool MayCancelVibration(const WindowIdentifier& aId) { 82 // Although only active windows may start vibrations, a window may 83 // cancel its own vibration even if it's no longer active. 84 // 85 // After a window is marked as inactive, it sends a CancelVibrate 86 // request. We want this request to cancel a playing vibration 87 // started by that window, so we certainly don't want to reject the 88 // cancellation request because the window is now inactive. 89 // 90 // But it could be the case that, after this window became inactive, 91 // some other window came along and started a vibration. We don't 92 // want this window's cancellation request to cancel that window's 93 // actively-playing vibration! 94 // 95 // To solve this problem, we keep track of the id of the last window 96 // to start a vibration, and only accepts cancellation requests from 97 // the same window. All other cancellation requests are ignored. 98 99 return InSandbox() || (*gLastIDToVibrate == aId.AsArray()); 100 } 101 102 } // namespace 103 104 void Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window) { 105 Vibrate(pattern, WindowIdentifier(window)); 106 } 107 108 void Vibrate(const nsTArray<uint32_t>& pattern, WindowIdentifier&& id) { 109 AssertMainThread(); 110 111 // Only active windows may start vibrations. If |id| hasn't gone 112 // through the IPC layer -- that is, if our caller is the outside 113 // world, not hal_proxy -- check whether the window is active. If 114 // |id| has gone through IPC, don't check the window's visibility; 115 // only the window corresponding to the bottommost process has its 116 // visibility state set correctly. 117 if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) { 118 HAL_LOG("Vibrate: Window is inactive, dropping vibrate."); 119 return; 120 } 121 122 RecordLastIDToVibrate(id); 123 124 // Don't forward our ID if we are not in the sandbox, because hal_impl 125 // doesn't need it, and we don't want it to be tempted to read it. The 126 // empty identifier will assert if it's used. 127 PROXY_IF_SANDBOXED( 128 Vibrate(pattern, InSandbox() ? std::move(id) : WindowIdentifier())); 129 } 130 131 void CancelVibrate(nsPIDOMWindowInner* window) { 132 CancelVibrate(WindowIdentifier(window)); 133 } 134 135 void CancelVibrate(WindowIdentifier&& id) { 136 AssertMainThread(); 137 138 if (MayCancelVibration(id)) { 139 // Don't forward our ID if we are not in the sandbox, because hal_impl 140 // doesn't need it, and we don't want it to be tempted to read it. The 141 // empty identifier will assert if it's used. 142 PROXY_IF_SANDBOXED( 143 CancelVibrate(InSandbox() ? std::move(id) : WindowIdentifier())); 144 } 145 } 146 147 template <class InfoType> 148 class ObserversManager { 149 public: 150 void AddObserver(Observer<InfoType>* aObserver) { 151 mObservers.AddObserver(aObserver); 152 153 if (mObservers.Length() == 1) { 154 EnableNotifications(); 155 } 156 } 157 158 void RemoveObserver(Observer<InfoType>* aObserver) { 159 bool removed = mObservers.RemoveObserver(aObserver); 160 if (!removed) { 161 return; 162 } 163 164 if (mObservers.Length() == 0) { 165 DisableNotifications(); 166 OnNotificationsDisabled(); 167 } 168 } 169 170 void BroadcastInformation(const InfoType& aInfo) { 171 mObservers.Broadcast(aInfo); 172 } 173 174 protected: 175 ~ObserversManager() { MOZ_ASSERT(mObservers.Length() == 0); } 176 177 virtual void EnableNotifications() = 0; 178 virtual void DisableNotifications() = 0; 179 virtual void OnNotificationsDisabled() {} 180 181 private: 182 mozilla::ObserverList<InfoType> mObservers; 183 }; 184 185 template <class InfoType> 186 class CachingObserversManager : public ObserversManager<InfoType> { 187 public: 188 InfoType GetCurrentInformation() { 189 if (mHasValidCache) { 190 return mInfo; 191 } 192 193 GetCurrentInformationInternal(&mInfo); 194 mHasValidCache = true; 195 return mInfo; 196 } 197 198 void CacheInformation(const InfoType& aInfo) { 199 mHasValidCache = true; 200 mInfo = aInfo; 201 } 202 203 void BroadcastCachedInformation() { this->BroadcastInformation(mInfo); } 204 205 protected: 206 virtual void GetCurrentInformationInternal(InfoType*) = 0; 207 208 void OnNotificationsDisabled() override { mHasValidCache = false; } 209 210 private: 211 InfoType mInfo; 212 bool mHasValidCache; 213 }; 214 215 class BatteryObserversManager final 216 : public CachingObserversManager<BatteryInformation> { 217 protected: 218 void EnableNotifications() override { 219 PROXY_IF_SANDBOXED(EnableBatteryNotifications()); 220 } 221 222 void DisableNotifications() override { 223 PROXY_IF_SANDBOXED(DisableBatteryNotifications()); 224 } 225 226 void GetCurrentInformationInternal(BatteryInformation* aInfo) override { 227 PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo)); 228 } 229 }; 230 231 class NetworkObserversManager final 232 : public CachingObserversManager<NetworkInformation> { 233 protected: 234 void EnableNotifications() override { 235 PROXY_IF_SANDBOXED(EnableNetworkNotifications()); 236 } 237 238 void DisableNotifications() override { 239 PROXY_IF_SANDBOXED(DisableNetworkNotifications()); 240 } 241 242 void GetCurrentInformationInternal(NetworkInformation* aInfo) override { 243 PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo)); 244 } 245 }; 246 247 class WakeLockObserversManager final 248 : public ObserversManager<WakeLockInformation> { 249 protected: 250 void EnableNotifications() override { 251 PROXY_IF_SANDBOXED(EnableWakeLockNotifications()); 252 } 253 254 void DisableNotifications() override { 255 PROXY_IF_SANDBOXED(DisableWakeLockNotifications()); 256 } 257 }; 258 259 typedef mozilla::ObserverList<SensorData> SensorObserverList; 260 StaticAutoPtr<SensorObserverList> sSensorObservers[NUM_SENSOR_TYPE]; 261 262 static SensorObserverList* GetSensorObservers(SensorType sensor_type) { 263 AssertMainThread(); 264 MOZ_ASSERT(sensor_type < NUM_SENSOR_TYPE); 265 266 if (!sSensorObservers[sensor_type]) { 267 sSensorObservers[sensor_type] = new SensorObserverList(); 268 } 269 270 return sSensorObservers[sensor_type]; 271 } 272 273 #define MOZ_IMPL_HAL_OBSERVER(name_) \ 274 StaticAutoPtr<name_##ObserversManager> s##name_##Observers; \ 275 \ 276 static name_##ObserversManager* name_##Observers() { \ 277 AssertMainThread(); \ 278 \ 279 if (!s##name_##Observers) { \ 280 MOZ_ASSERT(sInitialized); \ 281 s##name_##Observers = new name_##ObserversManager(); \ 282 } \ 283 \ 284 return s##name_##Observers; \ 285 } \ 286 \ 287 void Register##name_##Observer(name_##Observer* aObserver) { \ 288 AssertMainThread(); \ 289 name_##Observers()->AddObserver(aObserver); \ 290 } \ 291 \ 292 void Unregister##name_##Observer(name_##Observer* aObserver) { \ 293 AssertMainThread(); \ 294 name_##Observers()->RemoveObserver(aObserver); \ 295 } 296 297 MOZ_IMPL_HAL_OBSERVER(Battery) 298 299 void GetCurrentBatteryInformation(BatteryInformation* aInfo) { 300 *aInfo = BatteryObservers()->GetCurrentInformation(); 301 } 302 303 void NotifyBatteryChange(const BatteryInformation& aInfo) { 304 BatteryObservers()->CacheInformation(aInfo); 305 BatteryObservers()->BroadcastCachedInformation(); 306 } 307 308 void EnableSensorNotifications(SensorType aSensor) { 309 AssertMainThread(); 310 PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor)); 311 } 312 313 void DisableSensorNotifications(SensorType aSensor) { 314 AssertMainThread(); 315 PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor)); 316 } 317 318 void RegisterSensorObserver(SensorType aSensor, ISensorObserver* aObserver) { 319 SensorObserverList* observers = GetSensorObservers(aSensor); 320 321 observers->AddObserver(aObserver); 322 if (observers->Length() == 1) { 323 EnableSensorNotifications(aSensor); 324 } 325 } 326 327 void UnregisterSensorObserver(SensorType aSensor, ISensorObserver* aObserver) { 328 SensorObserverList* observers = GetSensorObservers(aSensor); 329 if (!observers->RemoveObserver(aObserver) || observers->Length() > 0) { 330 return; 331 } 332 DisableSensorNotifications(aSensor); 333 } 334 335 void NotifySensorChange(const SensorData& aSensorData) { 336 SensorObserverList* observers = GetSensorObservers(aSensorData.sensor()); 337 338 observers->Broadcast(aSensorData); 339 } 340 341 MOZ_IMPL_HAL_OBSERVER(Network) 342 343 void GetCurrentNetworkInformation(NetworkInformation* aInfo) { 344 *aInfo = NetworkObservers()->GetCurrentInformation(); 345 } 346 347 void NotifyNetworkChange(const NetworkInformation& aInfo) { 348 NetworkObservers()->CacheInformation(aInfo); 349 NetworkObservers()->BroadcastCachedInformation(); 350 } 351 352 MOZ_IMPL_HAL_OBSERVER(WakeLock) 353 354 void ModifyWakeLock(const nsAString& aTopic, WakeLockControl aLockAdjust, 355 WakeLockControl aHiddenAdjust) { 356 AssertMainThread(); 357 358 PROXY_IF_SANDBOXED(ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust)); 359 } 360 361 void GetWakeLockInfo(const nsAString& aTopic, 362 WakeLockInformation* aWakeLockInfo) { 363 AssertMainThread(); 364 PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo)); 365 } 366 367 void NotifyWakeLockChange(const WakeLockInformation& aInfo) { 368 AssertMainThread(); 369 WakeLockObservers()->BroadcastInformation(aInfo); 370 } 371 372 RefPtr<GenericNonExclusivePromise> LockScreenOrientation( 373 const ScreenOrientation& aOrientation) { 374 AssertMainThread(); 375 RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation), nullptr); 376 } 377 378 void UnlockScreenOrientation() { 379 AssertMainThread(); 380 PROXY_IF_SANDBOXED(UnlockScreenOrientation()); 381 } 382 383 void SetProcessPriority(int aPid, ProcessPriority aPriority) { 384 // n.b. The sandboxed implementation crashes; SetProcessPriority works only 385 // from the main process. 386 PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority)); 387 } 388 389 // From HalTypes.h. 390 const char* ProcessPriorityToString(ProcessPriority aPriority) { 391 switch (aPriority) { 392 case PROCESS_PRIORITY_PARENT_PROCESS: 393 return "PARENT_PROCESS"; 394 case PROCESS_PRIORITY_PREALLOC: 395 return "PREALLOC"; 396 case PROCESS_PRIORITY_FOREGROUND_HIGH: 397 return "FOREGROUND_HIGH"; 398 case PROCESS_PRIORITY_FOREGROUND: 399 return "FOREGROUND"; 400 case PROCESS_PRIORITY_FOREGROUND_KEYBOARD: 401 return "FOREGROUND_KEYBOARD"; 402 case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE: 403 return "BACKGROUND_PERCEIVABLE"; 404 case PROCESS_PRIORITY_BACKGROUND: 405 return "BACKGROUND"; 406 case PROCESS_PRIORITY_UNKNOWN: 407 return "UNKNOWN"; 408 default: 409 MOZ_ASSERT(false); 410 return "???"; 411 } 412 } 413 414 UniquePtr<hal::PerformanceHintSession> CreatePerformanceHintSession( 415 const nsTArray<PlatformThreadHandle>& aThreads, 416 mozilla::TimeDuration aTargetWorkDuration) { 417 return hal_impl::CreatePerformanceHintSession(aThreads, aTargetWorkDuration); 418 } 419 420 const Maybe<hal::HeterogeneousCpuInfo>& GetHeterogeneousCpuInfo() { 421 return hal_impl::GetHeterogeneousCpuInfo(); 422 } 423 424 void PerformHapticFeedback(int32_t aType) { 425 PROXY_IF_SANDBOXED(PerformHapticFeedback(aType)); 426 } 427 428 void Init() { 429 MOZ_ASSERT(!sInitialized); 430 431 if (!InSandbox()) { 432 gLastIDToVibrate = new WindowIdentifier::IDArrayType(); 433 } 434 435 WakeLockInit(); 436 437 sInitialized = true; 438 } 439 440 void Shutdown() { 441 MOZ_ASSERT(sInitialized); 442 443 gLastIDToVibrate = nullptr; 444 445 sBatteryObservers = nullptr; 446 sNetworkObservers = nullptr; 447 sWakeLockObservers = nullptr; 448 449 for (auto& sensorObserver : sSensorObservers) { 450 sensorObserver = nullptr; 451 } 452 453 sInitialized = false; 454 } 455 456 } // namespace mozilla::hal