namespace gfx { static StaticRefPtr<VRManagerChild> sVRManagerChildSingleton; static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton; void ReleaseVRManagerParentSingleton() { sVRManagerParentSingleton = nullptr; } VRManagerChild::VRManagerChild() : mInputFrameID(-1) { MOZ_COUNT_CTOR(VRManagerChild); MOZ_ASSERT(NS_IsMainThread()); } VRManagerChild::~VRManagerChild() { MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_DTOR(VRManagerChild); Transport* trans = GetTransport(); if (trans) { MOZ_ASSERT(XRE_GetIOMessageLoop()); RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(trans); XRE_GetIOMessageLoop()->PostTask(task.forget()); } } /*static*/ VRManagerChild* VRManagerChild::Get() { MOZ_ASSERT(sVRManagerChildSingleton); return sVRManagerChildSingleton; } /*static*/ VRManagerChild* VRManagerChild::StartUpInChildProcess(Transport* aTransport, ProcessId aOtherPid) { MOZ_ASSERT(NS_IsMainThread()); // There's only one VRManager per child process. MOZ_ASSERT(!sVRManagerChildSingleton); RefPtr<VRManagerChild> child(new VRManagerChild()); if (!child->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) { NS_RUNTIMEABORT("Couldn't Open() Compositor channel."); return nullptr; } sVRManagerChildSingleton = child; return sVRManagerChildSingleton; } /*static*/ void VRManagerChild::StartUpSameProcess() { NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); if (sVRManagerChildSingleton == nullptr) { sVRManagerChildSingleton = new VRManagerChild(); sVRManagerParentSingleton = VRManagerParent::CreateSameProcess(); sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(), mozilla::layers::CompositorThreadHolder::Loop(), mozilla::ipc::ChildSide); } } /*static*/ void VRManagerChild::ShutDown() { MOZ_ASSERT(NS_IsMainThread()); if (sVRManagerChildSingleton) { sVRManagerChildSingleton->Destroy(); sVRManagerChildSingleton = nullptr; } } /*static*/ void VRManagerChild::DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild) { aVRManagerChild->Close(); } void VRManagerChild::Destroy() { // This must not be called from the destructor! MOZ_ASSERT(mRefCnt != 0); // Keep ourselves alive until everything has been shut down RefPtr<VRManagerChild> selfRef = this; // The DeferredDestroyVRManager task takes ownership of // the VRManagerChild and will release it when it runs. MessageLoop::current()->PostTask( NewRunnableFunction(DeferredDestroy, selfRef)); } bool VRManagerChild::RecvUpdateDeviceInfo(nsTArray<VRDeviceUpdate>&& aDeviceUpdates) { // mDevices could be a hashed container for more scalability, but not worth // it now as we expect < 10 entries. nsTArray<RefPtr<VRDeviceProxy> > devices; for (auto& deviceUpdate: aDeviceUpdates) { bool isNewDevice = true; for (auto& device: mDevices) { if (device->GetDeviceInfo().GetDeviceID() == deviceUpdate.mDeviceInfo.GetDeviceID()) { device->UpdateDeviceInfo(deviceUpdate); devices.AppendElement(device); isNewDevice = false; break; } } if (isNewDevice) { if (deviceUpdate.mDeviceInfo.GetUseMainThreadOrientation()) { devices.AppendElement(new VRDeviceProxyOrientationFallBack(deviceUpdate)); } else { devices.AppendElement(new VRDeviceProxy(deviceUpdate)); } } } mDevices = devices; for (auto& nav: mNavigatorCallbacks) { nav->NotifyVRDevicesUpdated(); } mNavigatorCallbacks.Clear(); return true; } bool VRManagerChild::RecvUpdateDeviceSensors(nsTArray<VRSensorUpdate>&& aDeviceSensorUpdates) { // mDevices could be a hashed container for more scalability, but not worth // it now as we expect < 10 entries. for (auto& sensorUpdate: aDeviceSensorUpdates) { for (auto& device: mDevices) { if (device->GetDeviceInfo().GetDeviceID() == sensorUpdate.mDeviceID) { device->UpdateSensorState(sensorUpdate.mSensorState); mInputFrameID = sensorUpdate.mSensorState.inputFrameID; break; } } } return true; } bool VRManagerChild::GetVRDevices(nsTArray<RefPtr<VRDeviceProxy> >& aDevices) { aDevices = mDevices; return true; } bool VRManagerChild::RefreshVRDevicesWithCallback(dom::Navigator* aNavigator) { bool success = SendRefreshDevices(); if (success) { mNavigatorCallbacks.AppendElement(aNavigator); } return success; } int VRManagerChild::GetInputFrameID() { return mInputFrameID; } } // namespace gfx
namespace layers { StaticRefPtr<VideoBridgeChild> sVideoBridgeChildSingleton; /* static */ void VideoBridgeChild::Startup() { sVideoBridgeChildSingleton = new VideoBridgeChild(); RefPtr<VideoBridgeParent> parent = new VideoBridgeParent(); MessageLoop* loop = CompositorThreadHolder::Loop(); sVideoBridgeChildSingleton->Open(parent->GetIPCChannel(), loop, ipc::ChildSide); parent->SetOtherProcessId(base::GetCurrentProcId()); } /* static */ void VideoBridgeChild::Shutdown() { sVideoBridgeChildSingleton = nullptr; } VideoBridgeChild::VideoBridgeChild() : mMessageLoop(MessageLoop::current()) { sVideoBridgeChildSingleton = this; } VideoBridgeChild::~VideoBridgeChild() { } VideoBridgeChild* VideoBridgeChild::GetSingleton() { return sVideoBridgeChildSingleton; } bool VideoBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { return PVideoBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem); } bool VideoBridgeChild::AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { MOZ_ASSERT(CanSend()); return PVideoBridgeChild::AllocShmem(aSize, aType, aShmem); } bool VideoBridgeChild::DeallocShmem(ipc::Shmem& aShmem) { return PVideoBridgeChild::DeallocShmem(aShmem); } PTextureChild* VideoBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, const LayersBackend&, const TextureFlags&, const uint64_t& aSerial) { MOZ_ASSERT(CanSend()); return TextureClient::CreateIPDLActor(); } bool VideoBridgeChild::DeallocPTextureChild(PTextureChild* actor) { return TextureClient::DestroyIPDLActor(actor); } PTextureChild* VideoBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData, LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial) { MOZ_ASSERT(CanSend()); return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial); } bool VideoBridgeChild::IsSameProcess() const { return OtherPid() == base::GetCurrentProcId(); } } // namespace layers
// static void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient) { sImageBridgeChildSingleton->GetMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(&ReleaseTextureClientNow, aClient)); }
namespace system { class AutoMounter; /************************************************************************** * * Some helper functions for reading/writing files in /sys * **************************************************************************/ bool ReadSysFile(const char* aFilename, char* aBuf, size_t aBufSize) { int fd = open(aFilename, O_RDONLY); if (fd < 0) { ERR("Unable to open file '%s' for reading", aFilename); return false; } ScopedClose autoClose(fd); ssize_t bytesRead = read(fd, aBuf, aBufSize - 1); if (bytesRead < 0) { ERR("Unable to read from file '%s'", aFilename); return false; } if (aBuf[bytesRead - 1] == '\n') { bytesRead--; } aBuf[bytesRead] = '\0'; return true; } static bool ReadSysFile(const char* aFilename, bool* aVal) { char valBuf[20]; if (!ReadSysFile(aFilename, valBuf, sizeof(valBuf))) { return false; } int intVal; if (sscanf(valBuf, "%d", &intVal) != 1) { return false; } *aVal = (intVal != 0); return true; } /***************************************************************************/ inline const char* SwitchStateStr(const SwitchEvent& aEvent) { return aEvent.status() == SWITCH_STATE_ON ? "plugged" : "unplugged"; } /***************************************************************************/ static bool IsUsbCablePluggedIn() { #if 0 // Use this code when bug 745078 gets fixed (or use whatever the // appropriate method is) return GetCurrentSwitchEvent(SWITCH_USB) == SWITCH_STATE_ON; #else // Until then, just go read the file directly if (access(ICS_SYS_USB_STATE, F_OK) == 0) { char usbState[20]; return ReadSysFile(ICS_SYS_USB_STATE, usbState, sizeof(usbState)) && (strcmp(usbState, "CONFIGURED") == 0); } bool configured; return ReadSysFile(GB_SYS_USB_CONFIGURED, &configured) && configured; #endif } /***************************************************************************/ // The AutoVolumeManagerStateObserver allows the AutoMounter to know when // the volume manager changes state (i.e. it has finished initialization) class AutoVolumeManagerStateObserver : public VolumeManager::StateObserver { public: virtual void Notify(const VolumeManager::StateChangedEvent& aEvent); }; // The AutoVolumeEventObserver allows the AutoMounter to know about card // insertion and removal, as well as state changes in the volume. class AutoVolumeEventObserver : public Volume::EventObserver { public: virtual void Notify(Volume * const & aEvent); }; class AutoMounterResponseCallback : public VolumeResponseCallback { public: AutoMounterResponseCallback() : mErrorCount(0) { } protected: virtual void ResponseReceived(const VolumeCommand* aCommand); private: const static int kMaxErrorCount = 3; // Max number of errors before we give up int mErrorCount; }; /***************************************************************************/ class AutoMounter : public RefCounted<AutoMounter> { public: typedef nsTArray<RefPtr<Volume> > VolumeArray; AutoMounter() : mResponseCallback(new AutoMounterResponseCallback), mMode(AUTOMOUNTER_DISABLE) { VolumeManager::RegisterStateObserver(&mVolumeManagerStateObserver); Volume::RegisterObserver(&mVolumeEventObserver); VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); VolumeManager::VolumeArray::index_type i; for (i = 0; i < numVolumes; i++) { RefPtr<Volume> vol = VolumeManager::GetVolume(i); if (vol) { vol->RegisterObserver(&mVolumeEventObserver); // We need to pick up the intial value of the // ums.volume.NAME.enabled setting. AutoMounterSetting::CheckVolumeSettings(vol->Name()); } } DBG("Calling UpdateState from constructor"); UpdateState(); } ~AutoMounter() { VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); VolumeManager::VolumeArray::index_type volIndex; for (volIndex = 0; volIndex < numVolumes; volIndex++) { RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex); if (vol) { vol->UnregisterObserver(&mVolumeEventObserver); } } Volume::UnregisterObserver(&mVolumeEventObserver); VolumeManager::UnregisterStateObserver(&mVolumeManagerStateObserver); } void UpdateState(); const char* ModeStr(int32_t aMode) { switch (aMode) { case AUTOMOUNTER_DISABLE: return "Disable"; case AUTOMOUNTER_ENABLE: return "Enable"; case AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED: return "DisableWhenUnplugged"; } return "??? Unknown ???"; } void SetMode(int32_t aMode) { if ((aMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && (mMode == AUTOMOUNTER_DISABLE)) { // If it's already disabled, then leave it as disabled. // AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED implies "enabled until unplugged" aMode = AUTOMOUNTER_DISABLE; } if ((aMode == AUTOMOUNTER_DISABLE) && (mMode == AUTOMOUNTER_ENABLE) && IsUsbCablePluggedIn()) { // On many devices (esp non-Samsung), we can't force the disable, so we // need to defer until the USB cable is actually unplugged. // See bug 777043. // // Otherwise our attempt to disable it will fail, and we'll wind up in a bad // state where the AutoMounter thinks that Sharing has been turned off, but // the files are actually still being Shared because the attempt to unshare // failed. LOG("Attempting to disable UMS. Deferring until USB cable is unplugged."); aMode = AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED; } if (aMode != mMode) { LOG("Changing mode from '%s' to '%s'", ModeStr(mMode), ModeStr(aMode)); mMode = aMode; DBG("Calling UpdateState due to mode set to %d", mMode); UpdateState(); } } void SetSharingMode(const nsACString& aVolumeName, bool aAllowSharing) { RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); if (!vol) { return; } if (vol->IsSharingEnabled() == aAllowSharing) { return; } vol->SetSharingEnabled(aAllowSharing); DBG("Calling UpdateState due to volume %s shareing set to %d", vol->NameStr(), (int)aAllowSharing); UpdateState(); } private: AutoVolumeEventObserver mVolumeEventObserver; AutoVolumeManagerStateObserver mVolumeManagerStateObserver; RefPtr<VolumeResponseCallback> mResponseCallback; int32_t mMode; }; static StaticRefPtr<AutoMounter> sAutoMounter; /***************************************************************************/ void AutoVolumeManagerStateObserver::Notify(const VolumeManager::StateChangedEvent &) { LOG("VolumeManager state changed event: %s", VolumeManager::StateStr()); if (!sAutoMounter) { return; } DBG("Calling UpdateState due to VolumeManagerStateObserver"); sAutoMounter->UpdateState(); } void AutoVolumeEventObserver::Notify(Volume * const &) { if (!sAutoMounter) { return; } DBG("Calling UpdateState due to VolumeEventStateObserver"); sAutoMounter->UpdateState(); } void AutoMounterResponseCallback::ResponseReceived(const VolumeCommand* aCommand) { if (WasSuccessful()) { DBG("Calling UpdateState due to Volume::OnSuccess"); mErrorCount = 0; sAutoMounter->UpdateState(); return; } ERR("Command '%s' failed: %d '%s'", aCommand->CmdStr(), ResponseCode(), ResponseStr().get()); if (++mErrorCount < kMaxErrorCount) { DBG("Calling UpdateState due to VolumeResponseCallback::OnError"); sAutoMounter->UpdateState(); } } /***************************************************************************/ void AutoMounter::UpdateState() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); // If the following preconditions are met: // - UMS is available (i.e. compiled into the kernel) // - UMS is enabled // - AutoMounter is enabled // - USB cable is plugged in // then we will try to unmount and share // otherwise we will try to unshare and mount. if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { // The volume manager isn't in a ready state, so there // isn't anything else that we can do. LOG("UpdateState: VolumeManager not ready yet"); return; } if (mResponseCallback->IsPending()) { // We only deal with one outstanding volume command at a time, // so we need to wait for it to finish. return; } bool umsAvail = false; bool umsEnabled = false; if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) { umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0); char functionsStr[60]; umsEnabled = umsAvail && ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr)) && !!strstr(functionsStr, "mass_storage"); } else { umsAvail = ReadSysFile(GB_SYS_UMS_ENABLE, &umsEnabled); } bool usbCablePluggedIn = IsUsbCablePluggedIn(); bool enabled = (mMode == AUTOMOUNTER_ENABLE); if (mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) { enabled = usbCablePluggedIn; if (!usbCablePluggedIn) { mMode = AUTOMOUNTER_DISABLE; } } bool tryToShare = (umsAvail && umsEnabled && enabled && usbCablePluggedIn); LOG("UpdateState: umsAvail:%d umsEnabled:%d mode:%d usbCablePluggedIn:%d tryToShare:%d", umsAvail, umsEnabled, mMode, usbCablePluggedIn, tryToShare); VolumeArray::index_type volIndex; VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); for (volIndex = 0; volIndex < numVolumes; volIndex++) { RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex); Volume::STATE volState = vol->State(); if (vol->State() == nsIVolume::STATE_MOUNTED) { LOG("UpdateState: Volume %s is %s and %s @ %s gen %d locked %d sharing %c", vol->NameStr(), vol->StateStr(), vol->MediaPresent() ? "inserted" : "missing", vol->MountPoint().get(), vol->MountGeneration(), (int)vol->IsMountLocked(), vol->CanBeShared() ? (vol->IsSharingEnabled() ? 'y' : 'n') : 'x'); } else { LOG("UpdateState: Volume %s is %s and %s", vol->NameStr(), vol->StateStr(), vol->MediaPresent() ? "inserted" : "missing"); } if (!vol->MediaPresent()) { // No media - nothing we can do continue; } if (tryToShare && vol->IsSharingEnabled()) { // We're going to try to unmount and share the volumes switch (volState) { case nsIVolume::STATE_MOUNTED: { if (vol->IsMountLocked()) { // The volume is currently locked, so leave it in the mounted // state. LOGW("UpdateState: Mounted volume %s is locked, not sharing", vol->NameStr()); break; } // Check to see if there are any open files on the volume and // don't initiate the unmount while there are open files. OpenFileFinder::Info fileInfo; OpenFileFinder fileFinder(vol->MountPoint()); if (fileFinder.First(&fileInfo)) { LOGW("The following files are open under '%s'", vol->MountPoint().get()); do { LOGW(" PID: %d file: '%s' app: '%s' comm: '%s' exe: '%s'\n", fileInfo.mPid, fileInfo.mFileName.get(), fileInfo.mAppName.get(), fileInfo.mComm.get(), fileInfo.mExe.get()); } while (fileFinder.Next(&fileInfo)); LOGW("UpdateState: Mounted volume %s has open files, not sharing", vol->NameStr()); // Check again in 5 seconds to see if the files are closed. Since // we're trying to share the volume, this implies that we're // plugged into the PC via USB and this in turn implies that the // battery is charging, so we don't need to be too concerned about // wasting battery here. MessageLoopForIO::current()-> PostDelayedTask(FROM_HERE, NewRunnableMethod(this, &AutoMounter::UpdateState), 5000); break; } // Volume is mounted, we need to unmount before // we can share. LOG("UpdateState: Unmounting %s", vol->NameStr()); vol->StartUnmount(mResponseCallback); return; // UpdateState will be called again when the Unmount command completes } case nsIVolume::STATE_IDLE: { // Volume is unmounted. We can go ahead and share. LOG("UpdateState: Sharing %s", vol->NameStr()); vol->StartShare(mResponseCallback); return; // UpdateState will be called again when the Share command completes } default: { // Not in a state that we can do anything about. break; } } } else { // We're going to try and unshare and remount the volumes switch (volState) { case nsIVolume::STATE_SHARED: { // Volume is shared. We can go ahead and unshare. LOG("UpdateState: Unsharing %s", vol->NameStr()); vol->StartUnshare(mResponseCallback); return; // UpdateState will be called again when the Unshare command completes } case nsIVolume::STATE_IDLE: { // Volume is unmounted, try to mount. LOG("UpdateState: Mounting %s", vol->NameStr()); vol->StartMount(mResponseCallback); return; // UpdateState will be called again when Mount command completes } default: { // Not in a state that we can do anything about. break; } } } } } /***************************************************************************/ static void InitAutoMounterIOThread() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(!sAutoMounter); sAutoMounter = new AutoMounter(); } static void ShutdownAutoMounterIOThread() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); sAutoMounter = NULL; ShutdownVolumeManager(); } static void SetAutoMounterModeIOThread(const int32_t& aMode) { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(sAutoMounter); sAutoMounter->SetMode(aMode); } static void SetAutoMounterSharingModeIOThread(const nsCString& aVolumeName, const bool& aAllowSharing) { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(sAutoMounter); sAutoMounter->SetSharingMode(aVolumeName, aAllowSharing); } static void UsbCableEventIOThread() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (!sAutoMounter) { return; } DBG("Calling UpdateState due to USBCableEvent"); sAutoMounter->UpdateState(); } /************************************************************************** * * Public API * * Since the AutoMounter runs in IO Thread context, we need to switch * to IOThread context before we can do anything. * **************************************************************************/ class UsbCableObserver : public SwitchObserver, public RefCounted<UsbCableObserver> { public: UsbCableObserver() { RegisterSwitchObserver(SWITCH_USB, this); } ~UsbCableObserver() { UnregisterSwitchObserver(SWITCH_USB, this); } virtual void Notify(const SwitchEvent& aEvent) { DBG("UsbCable switch device: %d state: %s\n", aEvent.device(), SwitchStateStr(aEvent)); XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(UsbCableEventIOThread)); } }; static StaticRefPtr<UsbCableObserver> sUsbCableObserver; static StaticRefPtr<AutoMounterSetting> sAutoMounterSetting; void InitAutoMounter() { InitVolumeManager(); sAutoMounterSetting = new AutoMounterSetting(); XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(InitAutoMounterIOThread)); // Switch Observers need to run on the main thread, so we need to // start it here and have it send events to the AutoMounter running // on the IO Thread. sUsbCableObserver = new UsbCableObserver(); } void SetAutoMounterMode(int32_t aMode) { XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(SetAutoMounterModeIOThread, aMode)); } void SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing) { XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(SetAutoMounterSharingModeIOThread, aVolumeName, aAllowSharing)); } void ShutdownAutoMounter() { sAutoMounterSetting = NULL; sUsbCableObserver = NULL; XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(ShutdownAutoMounterIOThread)); } } // system
namespace mozilla { using namespace ipc; using namespace layers; using namespace gfx; // Only modified on the main-thread StaticRefPtr<nsIThread> sVideoDecoderChildThread; StaticRefPtr<AbstractThread> sVideoDecoderChildAbstractThread; // Only accessed from sVideoDecoderChildThread static StaticRefPtr<VideoDecoderManagerChild> sDecoderManager; static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks; /* static */ void VideoDecoderManagerChild::InitializeThread() { MOZ_ASSERT(NS_IsMainThread()); if (!sVideoDecoderChildThread) { RefPtr<nsIThread> childThread; nsresult rv = NS_NewNamedThread("VideoChild", getter_AddRefs(childThread)); NS_ENSURE_SUCCESS_VOID(rv); sVideoDecoderChildThread = childThread; sVideoDecoderChildAbstractThread = AbstractThread::CreateXPCOMThreadWrapper(childThread, false); sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>(); } } /* static */ void VideoDecoderManagerChild::InitForContent( Endpoint<PVideoDecoderManagerChild>&& aVideoManager) { InitializeThread(); sVideoDecoderChildThread->Dispatch( NewRunnableFunction("InitForContentRunnable", &Open, std::move(aVideoManager)), NS_DISPATCH_NORMAL); } /* static */ void VideoDecoderManagerChild::Shutdown() { MOZ_ASSERT(NS_IsMainThread()); if (sVideoDecoderChildThread) { sVideoDecoderChildThread->Dispatch( NS_NewRunnableFunction("VideoDecoderManagerChild::Shutdown", []() { if (sDecoderManager && sDecoderManager->CanSend()) { sDecoderManager->Close(); sDecoderManager = nullptr; } }), NS_DISPATCH_NORMAL); sVideoDecoderChildAbstractThread = nullptr; sVideoDecoderChildThread->Shutdown(); sVideoDecoderChildThread = nullptr; sRecreateTasks = nullptr; } } void VideoDecoderManagerChild::RunWhenRecreated( already_AddRefed<Runnable> aTask) { MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); // If we've already been recreated, then run the task immediately. if (sDecoderManager && sDecoderManager != this && sDecoderManager->CanSend()) { RefPtr<Runnable> task = aTask; task->Run(); } else { sRecreateTasks->AppendElement(aTask); } } /* static */ VideoDecoderManagerChild* VideoDecoderManagerChild::GetSingleton() { MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); return sDecoderManager; } /* static */ nsIThread* VideoDecoderManagerChild::GetManagerThread() { return sVideoDecoderChildThread; } /* static */ AbstractThread* VideoDecoderManagerChild::GetManagerAbstractThread() { return sVideoDecoderChildAbstractThread; } PVideoDecoderChild* VideoDecoderManagerChild::AllocPVideoDecoderChild( const VideoInfo& aVideoInfo, const float& aFramerate, const CreateDecoderParams::OptionSet& aOptions, const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess, nsCString* /* not used */, nsCString* /* not used */, nsCString* /* not used */) { return new VideoDecoderChild(); } bool VideoDecoderManagerChild::DeallocPVideoDecoderChild( PVideoDecoderChild* actor) { VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor); child->IPDLActorDestroyed(); return true; } void VideoDecoderManagerChild::Open( Endpoint<PVideoDecoderManagerChild>&& aEndpoint) { // Make sure we always dispatch everything in sRecreateTasks, even if we // fail since this is as close to being recreated as we will ever be. sDecoderManager = nullptr; if (aEndpoint.IsValid()) { RefPtr<VideoDecoderManagerChild> manager = new VideoDecoderManagerChild(); if (aEndpoint.Bind(manager)) { sDecoderManager = manager; manager->InitIPDL(); } } for (Runnable* task : *sRecreateTasks) { task->Run(); } sRecreateTasks->Clear(); } void VideoDecoderManagerChild::InitIPDL() { mCanSend = true; mIPDLSelfRef = this; } void VideoDecoderManagerChild::ActorDestroy(ActorDestroyReason aWhy) { mCanSend = false; } void VideoDecoderManagerChild::DeallocPVideoDecoderManagerChild() { mIPDLSelfRef = nullptr; } bool VideoDecoderManagerChild::CanSend() { MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread()); return mCanSend; } bool VideoDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem) { if (NS_GetCurrentThread() != sVideoDecoderChildThread) { RefPtr<VideoDecoderManagerChild> self = this; mozilla::ipc::Shmem shmem = aShmem; sVideoDecoderChildThread->Dispatch( NS_NewRunnableFunction("VideoDecoderManagerChild::DeallocShmem", [self, shmem]() { if (self->CanSend()) { mozilla::ipc::Shmem shmemCopy = shmem; self->DeallocShmem(shmemCopy); } }), NS_DISPATCH_NORMAL); return true; } return PVideoDecoderManagerChild::DeallocShmem(aShmem); } struct SurfaceDescriptorUserData { SurfaceDescriptorUserData(VideoDecoderManagerChild* aAllocator, SurfaceDescriptor& aSD) : mAllocator(aAllocator), mSD(aSD) {} ~SurfaceDescriptorUserData() { DestroySurfaceDescriptor(mAllocator, &mSD); } RefPtr<VideoDecoderManagerChild> mAllocator; SurfaceDescriptor mSD; }; void DeleteSurfaceDescriptorUserData(void* aClosure) { SurfaceDescriptorUserData* sd = reinterpret_cast<SurfaceDescriptorUserData*>(aClosure); delete sd; } already_AddRefed<SourceSurface> VideoDecoderManagerChild::Readback( const SurfaceDescriptorGPUVideo& aSD) { // We can't use NS_DISPATCH_SYNC here since that can spin the event // loop while it waits. This function can be called from JS and we // don't want that to happen. SynchronousTask task("Readback sync"); RefPtr<VideoDecoderManagerChild> ref = this; SurfaceDescriptor sd; if (NS_FAILED(sVideoDecoderChildThread->Dispatch( NS_NewRunnableFunction("VideoDecoderManagerChild::Readback", [&]() { AutoCompleteTask complete(&task); if (ref->CanSend()) { ref->SendReadback(aSD, &sd); } }), NS_DISPATCH_NORMAL))) { return nullptr; } task.Wait(); if (!IsSurfaceDescriptorValid(sd)) { return nullptr; } RefPtr<DataSourceSurface> source = GetSurfaceForDescriptor(sd); if (!source) { DestroySurfaceDescriptor(this, &sd); NS_WARNING("Failed to map SurfaceDescriptor in Readback"); return nullptr; } static UserDataKey sSurfaceDescriptor; source->AddUserData(&sSurfaceDescriptor, new SurfaceDescriptorUserData(this, sd), DeleteSurfaceDescriptorUserData); return source.forget(); } void VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo( const SurfaceDescriptorGPUVideo& aSD) { RefPtr<VideoDecoderManagerChild> ref = this; SurfaceDescriptorGPUVideo sd = std::move(aSD); sVideoDecoderChildThread->Dispatch( NS_NewRunnableFunction( "VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo", [ref, sd]() { if (ref->CanSend()) { ref->SendDeallocateSurfaceDescriptorGPUVideo(sd); } }), NS_DISPATCH_NORMAL); } void VideoDecoderManagerChild::HandleFatalError(const char* aMsg) const { dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); } } // namespace mozilla
namespace system { static StaticRefPtr<VolumeManager> sVolumeManager; VolumeManager::STATE VolumeManager::mState = VolumeManager::UNINITIALIZED; VolumeManager::StateObserverList VolumeManager::mStateObserverList; /***************************************************************************/ VolumeManager::VolumeManager() : LineWatcher('\0', kRcvBufSize), mSocket(-1), mCommandPending(false) { DBG("VolumeManager constructor called"); } VolumeManager::~VolumeManager() { } //static void VolumeManager::Dump(const char* aLabel) { if (!sVolumeManager) { LOG("%s: sVolumeManager == null", aLabel); return; } VolumeArray::size_type numVolumes = NumVolumes(); VolumeArray::index_type volIndex; for (volIndex = 0; volIndex < numVolumes; volIndex++) { RefPtr<Volume> vol = GetVolume(volIndex); vol->Dump(aLabel); } } //static size_t VolumeManager::NumVolumes() { if (!sVolumeManager) { return 0; } return sVolumeManager->mVolumeArray.Length(); } //static TemporaryRef<Volume> VolumeManager::GetVolume(size_t aIndex) { MOZ_ASSERT(aIndex < NumVolumes()); return sVolumeManager->mVolumeArray[aIndex]; } //static VolumeManager::STATE VolumeManager::State() { return mState; } //static const char * VolumeManager::StateStr(VolumeManager::STATE aState) { switch (aState) { case UNINITIALIZED: return "Uninitialized"; case STARTING: return "Starting"; case VOLUMES_READY: return "Volumes Ready"; } return "???"; } //static void VolumeManager::SetState(STATE aNewState) { if (mState != aNewState) { LOG("changing state from '%s' to '%s'", StateStr(mState), StateStr(aNewState)); mState = aNewState; mStateObserverList.Broadcast(StateChangedEvent()); } } //static void VolumeManager::RegisterStateObserver(StateObserver* aObserver) { mStateObserverList.AddObserver(aObserver); } //static void VolumeManager::UnregisterStateObserver(StateObserver* aObserver) { mStateObserverList.RemoveObserver(aObserver); } //static TemporaryRef<Volume> VolumeManager::FindVolumeByName(const nsCSubstring& aName) { if (!sVolumeManager) { return nullptr; } VolumeArray::size_type numVolumes = NumVolumes(); VolumeArray::index_type volIndex; for (volIndex = 0; volIndex < numVolumes; volIndex++) { RefPtr<Volume> vol = GetVolume(volIndex); if (vol->Name().Equals(aName)) { return vol; } } return nullptr; } //static TemporaryRef<Volume> VolumeManager::FindAddVolumeByName(const nsCSubstring& aName) { RefPtr<Volume> vol = FindVolumeByName(aName); if (vol) { return vol; } // No volume found, create and add a new one. vol = new Volume(aName); sVolumeManager->mVolumeArray.AppendElement(vol); return vol; } //static void VolumeManager::InitConfig() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); // This function uses /system/etc/volume.cfg to add additional volumes // to the Volume Manager. // // This is useful on devices like the Nexus 4, which have no physical sd card // or dedicated partition. // // The format of the volume.cfg file is as follows: // create volume-name mount-point // Blank lines and lines starting with the hash character "#" will be ignored. ScopedCloseFile fp; int n = 0; char line[255]; char *command, *volNamePtr, *mountPointPtr, *save_ptr; const char *filename = "/system/etc/volume.cfg"; if (!(fp = fopen(filename, "r"))) { LOG("Unable to open volume configuration file '%s' - ignoring", filename); return; } while(fgets(line, sizeof(line), fp)) { const char *delim = " \t\n"; n++; if (line[0] == '#') continue; if (!(command = strtok_r(line, delim, &save_ptr))) { // Blank line - ignore continue; } if (!strcmp(command, "create")) { if (!(volNamePtr = strtok_r(nullptr, delim, &save_ptr))) { ERR("No vol_name in %s line %d", filename, n); continue; } if (!(mountPointPtr = strtok_r(nullptr, delim, &save_ptr))) { ERR("No mount point for volume '%s'. %s line %d", volNamePtr, filename, n); continue; } nsCString mountPoint(mountPointPtr); nsCString volName(volNamePtr); RefPtr<Volume> vol = FindAddVolumeByName(volName); vol->SetFakeVolume(mountPoint); } else { ERR("Unrecognized command: '%s'", command); } } } class VolumeListCallback : public VolumeResponseCallback { virtual void ResponseReceived(const VolumeCommand* aCommand) { switch (ResponseCode()) { case ::ResponseCode::VolumeListResult: { // Each line will look something like: // // sdcard /mnt/sdcard 1 // // So for each volume that we get back, we update any volumes that // we have of the same name, or add new ones if they don't exist. nsCWhitespaceTokenizer tokenizer(ResponseStr()); nsDependentCSubstring volName(tokenizer.nextToken()); RefPtr<Volume> vol = VolumeManager::FindAddVolumeByName(volName); vol->HandleVoldResponse(ResponseCode(), tokenizer); break; } case ::ResponseCode::CommandOkay: { // We've received the list of volumes. Now read the Volume.cfg // file to perform customizations, and then tell everybody // that we're ready for business. VolumeManager::InitConfig(); VolumeManager::Dump("READY"); VolumeManager::SetState(VolumeManager::VOLUMES_READY); break; } } } }; bool VolumeManager::OpenSocket() { SetState(STARTING); if ((mSocket.rwget() = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0) { ERR("Error connecting to vold: (%s) - will retry", strerror(errno)); return false; } // add FD_CLOEXEC flag int flags = fcntl(mSocket.get(), F_GETFD); if (flags == -1) { return false; } flags |= FD_CLOEXEC; if (fcntl(mSocket.get(), F_SETFD, flags) == -1) { return false; } // set non-blocking if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) { return false; } if (!MessageLoopForIO::current()-> WatchFileDescriptor(mSocket.get(), true, MessageLoopForIO::WATCH_READ, &mReadWatcher, this)) { return false; } LOG("Connected to vold"); PostCommand(new VolumeListCommand(new VolumeListCallback)); return true; } //static void VolumeManager::PostCommand(VolumeCommand* aCommand) { if (!sVolumeManager) { ERR("VolumeManager not initialized. Dropping command '%s'", aCommand->Data()); return; } aCommand->SetPending(true); DBG("Sending command '%s'", aCommand->Data()); // vold can only process one command at a time, so add our command // to the end of the command queue. sVolumeManager->mCommands.push(aCommand); if (!sVolumeManager->mCommandPending) { // There aren't any commands currently being processed, so go // ahead and kick this one off. sVolumeManager->mCommandPending = true; sVolumeManager->WriteCommandData(); } } /*************************************************************************** * The WriteCommandData initiates sending of a command to vold. Since * we're running on the IOThread and not allowed to block, WriteCommandData * will write as much data as it can, and if not all of the data can be * written then it will setup a file descriptor watcher and * OnFileCanWriteWithoutBlocking will call WriteCommandData to write out * more of the command data. */ void VolumeManager::WriteCommandData() { if (mCommands.size() == 0) { return; } VolumeCommand* cmd = mCommands.front(); if (cmd->BytesRemaining() == 0) { // All bytes have been written. We're waiting for a response. return; } // There are more bytes left to write. Try to write them all. ssize_t bytesWritten = write(mSocket.get(), cmd->Data(), cmd->BytesRemaining()); if (bytesWritten < 0) { ERR("Failed to write %d bytes to vold socket", cmd->BytesRemaining()); Restart(); return; } DBG("Wrote %ld bytes (of %d)", bytesWritten, cmd->BytesRemaining()); cmd->ConsumeBytes(bytesWritten); if (cmd->BytesRemaining() == 0) { return; } // We were unable to write all of the command bytes. Setup a watcher // so we'll get called again when we can write without blocking. if (!MessageLoopForIO::current()-> WatchFileDescriptor(mSocket.get(), false, // one-shot MessageLoopForIO::WATCH_WRITE, &mWriteWatcher, this)) { ERR("Failed to setup write watcher for vold socket"); Restart(); } } void VolumeManager::OnLineRead(int aFd, nsDependentCSubstring& aMessage) { MOZ_ASSERT(aFd == mSocket.get()); char* endPtr; int responseCode = strtol(aMessage.Data(), &endPtr, 10); if (*endPtr == ' ') { endPtr++; } // Now fish out the rest of the line after the response code nsDependentCString responseLine(endPtr, aMessage.Length() - (endPtr - aMessage.Data())); DBG("Rcvd: %d '%s'", responseCode, responseLine.Data()); if (responseCode >= ::ResponseCode::UnsolicitedInformational) { // These are unsolicited broadcasts. We intercept these and process // them ourselves HandleBroadcast(responseCode, responseLine); } else { // Everything else is considered to be part of the command response. if (mCommands.size() > 0) { VolumeCommand* cmd = mCommands.front(); cmd->HandleResponse(responseCode, responseLine); if (responseCode >= ::ResponseCode::CommandOkay) { // That's a terminating response. We can remove the command. mCommands.pop(); mCommandPending = false; // Start the next command, if there is one. WriteCommandData(); } } else { ERR("Response with no command"); } } } void VolumeManager::OnFileCanWriteWithoutBlocking(int aFd) { MOZ_ASSERT(aFd == mSocket.get()); WriteCommandData(); } void VolumeManager::HandleBroadcast(int aResponseCode, nsCString& aResponseLine) { // Format of the line is something like: // // Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted) // // So we parse out the volume name and the state after the string " to " nsCWhitespaceTokenizer tokenizer(aResponseLine); tokenizer.nextToken(); // The word "Volume" nsDependentCSubstring volName(tokenizer.nextToken()); RefPtr<Volume> vol = FindVolumeByName(volName); if (!vol) { return; } vol->HandleVoldResponse(aResponseCode, tokenizer); } void VolumeManager::Restart() { mReadWatcher.StopWatchingFileDescriptor(); mWriteWatcher.StopWatchingFileDescriptor(); while (!mCommands.empty()) { mCommands.pop(); } mCommandPending = false; mSocket.dispose(); Start(); } //static void VolumeManager::Start() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (!sVolumeManager) { return; } SetState(STARTING); if (!sVolumeManager->OpenSocket()) { // Socket open failed, try again in a second. MessageLoopForIO::current()-> PostDelayedTask(FROM_HERE, NewRunnableFunction(VolumeManager::Start), 1000); } } void VolumeManager::OnError() { Restart(); } /***************************************************************************/ static void InitVolumeManagerIOThread() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(!sVolumeManager); sVolumeManager = new VolumeManager(); VolumeManager::Start(); InitVolumeServiceTestIOThread(); } static void ShutdownVolumeManagerIOThread() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); sVolumeManager = nullptr; } /************************************************************************** * * Public API * * Since the VolumeManager runs in IO Thread context, we need to switch * to IOThread context before we can do anything. * **************************************************************************/ void InitVolumeManager() { XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(InitVolumeManagerIOThread)); } void ShutdownVolumeManager() { ShutdownVolumeServiceTest(); XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(ShutdownVolumeManagerIOThread)); } } // system
namespace gfx { static StaticRefPtr<VRManagerChild> sVRManagerChildSingleton; static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton; void ReleaseVRManagerParentSingleton() { sVRManagerParentSingleton = nullptr; } VRManagerChild::VRManagerChild() : TextureForwarder() , mDisplaysInitialized(false) , mMessageLoop(MessageLoop::current()) , mFrameRequestCallbackCounter(0) , mBackend(layers::LayersBackend::LAYERS_NONE) , mPromiseID(0) , mVRMockDisplay(nullptr) { MOZ_ASSERT(NS_IsMainThread()); mStartTimeStamp = TimeStamp::Now(); } VRManagerChild::~VRManagerChild() { MOZ_ASSERT(NS_IsMainThread()); } /*static*/ void VRManagerChild::IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier) { if (sVRManagerChildSingleton) { sVRManagerChildSingleton->mBackend = aIdentifier.mParentBackend; sVRManagerChildSingleton->mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(aIdentifier.mSyncHandle); } } layers::LayersBackend VRManagerChild::GetBackendType() const { return mBackend; } /*static*/ VRManagerChild* VRManagerChild::Get() { MOZ_ASSERT(sVRManagerChildSingleton); return sVRManagerChildSingleton; } /* static */ bool VRManagerChild::IsCreated() { return !!sVRManagerChildSingleton; } /* static */ bool VRManagerChild::InitForContent(Endpoint<PVRManagerChild>&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sVRManagerChildSingleton); RefPtr<VRManagerChild> child(new VRManagerChild()); if (!aEndpoint.Bind(child)) { NS_RUNTIMEABORT("Couldn't Open() Compositor channel."); return false; } sVRManagerChildSingleton = child; return true; } /* static */ bool VRManagerChild::ReinitForContent(Endpoint<PVRManagerChild>&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); ShutDown(); return InitForContent(Move(aEndpoint)); } /*static*/ void VRManagerChild::InitSameProcess() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sVRManagerChildSingleton); sVRManagerChildSingleton = new VRManagerChild(); sVRManagerParentSingleton = VRManagerParent::CreateSameProcess(); sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(), mozilla::layers::CompositorThreadHolder::Loop(), mozilla::ipc::ChildSide); } /* static */ void VRManagerChild::InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sVRManagerChildSingleton); sVRManagerChildSingleton = new VRManagerChild(); if (!aEndpoint.Bind(sVRManagerChildSingleton)) { NS_RUNTIMEABORT("Couldn't Open() Compositor channel."); return; } } /*static*/ void VRManagerChild::ShutDown() { MOZ_ASSERT(NS_IsMainThread()); if (sVRManagerChildSingleton) { sVRManagerChildSingleton->Destroy(); sVRManagerChildSingleton = nullptr; } } /*static*/ void VRManagerChild::DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild) { aVRManagerChild->Close(); } void VRManagerChild::Destroy() { mTexturesWaitingRecycled.Clear(); // Keep ourselves alive until everything has been shut down RefPtr<VRManagerChild> selfRef = this; // The DeferredDestroyVRManager task takes ownership of // the VRManagerChild and will release it when it runs. MessageLoop::current()->PostTask( NewRunnableFunction(DeferredDestroy, selfRef)); } layers::PTextureChild* VRManagerChild::AllocPTextureChild(const SurfaceDescriptor&, const LayersBackend&, const TextureFlags&, const uint64_t&) { return TextureClient::CreateIPDLActor(); } bool VRManagerChild::DeallocPTextureChild(PTextureChild* actor) { return TextureClient::DestroyIPDLActor(actor); } PVRLayerChild* VRManagerChild::AllocPVRLayerChild(const uint32_t& aDisplayID, const float& aLeftEyeX, const float& aLeftEyeY, const float& aLeftEyeWidth, const float& aLeftEyeHeight, const float& aRightEyeX, const float& aRightEyeY, const float& aRightEyeWidth, const float& aRightEyeHeight, const uint32_t& aGroup) { return VRLayerChild::CreateIPDLActor(); } bool VRManagerChild::DeallocPVRLayerChild(PVRLayerChild* actor) { return VRLayerChild::DestroyIPDLActor(actor); } void VRManagerChild::UpdateDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayUpdates) { nsTArray<uint32_t> disconnectedDisplays; nsTArray<uint32_t> connectedDisplays; // Check if any displays have been disconnected for (auto& display : mDisplays) { bool found = false; for (auto& displayUpdate : aDisplayUpdates) { if (display->GetDisplayInfo().GetDisplayID() == displayUpdate.GetDisplayID()) { found = true; break; } } if (!found) { display->NotifyDisconnected(); disconnectedDisplays.AppendElement(display->GetDisplayInfo().GetDisplayID()); } } // mDisplays could be a hashed container for more scalability, but not worth // it now as we expect < 10 entries. nsTArray<RefPtr<VRDisplayClient>> displays; for (VRDisplayInfo& displayUpdate : aDisplayUpdates) { bool isNewDisplay = true; for (auto& display : mDisplays) { const VRDisplayInfo& prevInfo = display->GetDisplayInfo(); if (prevInfo.GetDisplayID() == displayUpdate.GetDisplayID()) { if (displayUpdate.GetIsConnected() && !prevInfo.GetIsConnected()) { connectedDisplays.AppendElement(displayUpdate.GetDisplayID()); } if (!displayUpdate.GetIsConnected() && prevInfo.GetIsConnected()) { disconnectedDisplays.AppendElement(displayUpdate.GetDisplayID()); } display->UpdateDisplayInfo(displayUpdate); displays.AppendElement(display); isNewDisplay = false; break; } } if (isNewDisplay) { displays.AppendElement(new VRDisplayClient(displayUpdate)); connectedDisplays.AppendElement(displayUpdate.GetDisplayID()); } } mDisplays = displays; // We wish to fire the events only after mDisplays is updated for (uint32_t displayID : disconnectedDisplays) { FireDOMVRDisplayDisconnectEvent(displayID); } for (uint32_t displayID : connectedDisplays) { FireDOMVRDisplayConnectEvent(displayID); } mDisplaysInitialized = true; } mozilla::ipc::IPCResult VRManagerChild::RecvUpdateDisplayInfo(nsTArray<VRDisplayInfo>&& aDisplayUpdates) { UpdateDisplayInfo(aDisplayUpdates); for (auto& windowId : mNavigatorCallbacks) { /** We must call NotifyVRDisplaysUpdated for every * window's Navigator in mNavigatorCallbacks to ensure that * the promise returned by Navigator.GetVRDevices * can resolve. This must happen even if no changes * to VRDisplays have been detected here. */ nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(windowId); if (!window) { continue; } dom::Navigator* nav = window->Navigator(); if (!nav) { continue; } nav->NotifyVRDisplaysUpdated(); } mNavigatorCallbacks.Clear(); return IPC_OK(); } bool VRManagerChild::GetVRDisplays(nsTArray<RefPtr<VRDisplayClient>>& aDisplays) { aDisplays = mDisplays; return true; } bool VRManagerChild::RefreshVRDisplaysWithCallback(uint64_t aWindowId) { bool success = SendRefreshDisplays(); if (success) { mNavigatorCallbacks.AppendElement(aWindowId); } return success; } void VRManagerChild::CreateVRServiceTestDisplay(const nsCString& aID, dom::Promise* aPromise) { SendCreateVRServiceTestDisplay(aID, mPromiseID); mPromiseList.Put(mPromiseID, aPromise); ++mPromiseID; } void VRManagerChild::CreateVRServiceTestController(const nsCString& aID, dom::Promise* aPromise) { SendCreateVRServiceTestController(aID, mPromiseID); mPromiseList.Put(mPromiseID, aPromise); ++mPromiseID; } mozilla::ipc::IPCResult VRManagerChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) { for (InfallibleTArray<AsyncParentMessageData>::index_type i = 0; i < aMessages.Length(); ++i) { const AsyncParentMessageData& message = aMessages[i]; switch (message.type()) { case AsyncParentMessageData::TOpNotifyNotUsed: { const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed(); NotifyNotUsed(op.TextureId(), op.fwdTransactionId()); break; } default: NS_ERROR("unknown AsyncParentMessageData type"); return IPC_FAIL_NO_REASON(this); } } return IPC_OK(); } PTextureChild* VRManagerChild::CreateTexture(const SurfaceDescriptor& aSharedData, LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial, wr::MaybeExternalImageId& aExternalImageId, nsIEventTarget* aTarget) { return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial); } void VRManagerChild::CancelWaitForRecycle(uint64_t aTextureId) { RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); if (!client) { return; } mTexturesWaitingRecycled.Remove(aTextureId); } void VRManagerChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId) { RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); if (!client) { return; } mTexturesWaitingRecycled.Remove(aTextureId); } bool VRManagerChild::AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { return PVRManagerChild::AllocShmem(aSize, aType, aShmem); } bool VRManagerChild::AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { return PVRManagerChild::AllocUnsafeShmem(aSize, aType, aShmem); } bool VRManagerChild::DeallocShmem(ipc::Shmem& aShmem) { return PVRManagerChild::DeallocShmem(aShmem); } PVRLayerChild* VRManagerChild::CreateVRLayer(uint32_t aDisplayID, const Rect& aLeftEyeRect, const Rect& aRightEyeRect, nsIEventTarget* aTarget, uint32_t aGroup) { PVRLayerChild* vrLayerChild = AllocPVRLayerChild(aDisplayID, aLeftEyeRect.x, aLeftEyeRect.y, aLeftEyeRect.width, aLeftEyeRect.height, aRightEyeRect.x, aRightEyeRect.y, aRightEyeRect.width, aRightEyeRect.height, aGroup); // Do the DOM labeling. if (aTarget) { SetEventTargetForActor(vrLayerChild, aTarget); MOZ_ASSERT(vrLayerChild->GetActorEventTarget()); } return SendPVRLayerConstructor(vrLayerChild, aDisplayID, aLeftEyeRect.x, aLeftEyeRect.y, aLeftEyeRect.width, aLeftEyeRect.height, aRightEyeRect.x, aRightEyeRect.y, aRightEyeRect.width, aRightEyeRect.height, aGroup); } // XXX TODO - VRManagerChild::FrameRequest is the same as nsIDocument::FrameRequest, should we consolodate these? struct VRManagerChild::FrameRequest { FrameRequest(mozilla::dom::FrameRequestCallback& aCallback, int32_t aHandle) : mCallback(&aCallback), mHandle(aHandle) {} // Conversion operator so that we can append these to a // FrameRequestCallbackList operator const RefPtr<mozilla::dom::FrameRequestCallback>& () const { return mCallback; } // Comparator operators to allow RemoveElementSorted with an // integer argument on arrays of FrameRequest bool operator==(int32_t aHandle) const { return mHandle == aHandle; } bool operator<(int32_t aHandle) const { return mHandle < aHandle; } RefPtr<mozilla::dom::FrameRequestCallback> mCallback; int32_t mHandle; }; nsresult VRManagerChild::ScheduleFrameRequestCallback(mozilla::dom::FrameRequestCallback& aCallback, int32_t *aHandle) { if (mFrameRequestCallbackCounter == INT32_MAX) { // Can't increment without overflowing; bail out return NS_ERROR_NOT_AVAILABLE; } int32_t newHandle = ++mFrameRequestCallbackCounter; DebugOnly<FrameRequest*> request = mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle)); NS_ASSERTION(request, "This is supposed to be infallible!"); *aHandle = newHandle; return NS_OK; } void VRManagerChild::CancelFrameRequestCallback(int32_t aHandle) { // mFrameRequestCallbacks is stored sorted by handle mFrameRequestCallbacks.RemoveElementSorted(aHandle); } mozilla::ipc::IPCResult VRManagerChild::RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent) { // VRManagerChild could be at other processes, but GamepadManager // only exists at the content process or the same process // in non-e10s mode. MOZ_ASSERT(XRE_IsContentProcess() || IsSameProcess()); RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService()); if (gamepadManager) { gamepadManager->Update(aGamepadEvent); } return IPC_OK(); } mozilla::ipc::IPCResult VRManagerChild::RecvReplyCreateVRServiceTestDisplay(const nsCString& aID, const uint32_t& aPromiseID, const uint32_t& aDeviceID) { RefPtr<dom::Promise> p; if (!mPromiseList.Get(aPromiseID, getter_AddRefs(p))) { MOZ_CRASH("We should always have a promise."); } // We only allow one VRMockDisplay in VR tests. if (!mVRMockDisplay) { mVRMockDisplay = new VRMockDisplay(aID, aDeviceID); } p->MaybeResolve(mVRMockDisplay); mPromiseList.Remove(aPromiseID); return IPC_OK(); } mozilla::ipc::IPCResult VRManagerChild::RecvReplyCreateVRServiceTestController(const nsCString& aID, const uint32_t& aPromiseID, const uint32_t& aDeviceID) { RefPtr<dom::Promise> p; if (!mPromiseList.Get(aPromiseID, getter_AddRefs(p))) { MOZ_CRASH("We should always have a promise."); } p->MaybeResolve(new VRMockController(aID, aDeviceID)); mPromiseList.Remove(aPromiseID); return IPC_OK(); } void VRManagerChild::RunFrameRequestCallbacks() { TimeStamp nowTime = TimeStamp::Now(); mozilla::TimeDuration duration = nowTime - mStartTimeStamp; DOMHighResTimeStamp timeStamp = duration.ToMilliseconds(); nsTArray<FrameRequest> callbacks; callbacks.AppendElements(mFrameRequestCallbacks); mFrameRequestCallbacks.Clear(); for (auto& callback : callbacks) { callback.mCallback->Call(timeStamp); } } void VRManagerChild::FireDOMVRDisplayMountedEvent(uint32_t aDisplayID) { nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>( "gfx::VRManagerChild::FireDOMVRDisplayMountedEventInternal", this, &VRManagerChild::FireDOMVRDisplayMountedEventInternal, aDisplayID)); } void VRManagerChild::FireDOMVRDisplayUnmountedEvent(uint32_t aDisplayID) { nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>( "gfx::VRManagerChild::FireDOMVRDisplayUnmountedEventInternal", this, &VRManagerChild::FireDOMVRDisplayUnmountedEventInternal, aDisplayID)); } void VRManagerChild::FireDOMVRDisplayConnectEvent(uint32_t aDisplayID) { nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>( "gfx::VRManagerChild::FireDOMVRDisplayConnectEventInternal", this, &VRManagerChild::FireDOMVRDisplayConnectEventInternal, aDisplayID)); } void VRManagerChild::FireDOMVRDisplayDisconnectEvent(uint32_t aDisplayID) { nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>( "gfx::VRManagerChild::FireDOMVRDisplayDisconnectEventInternal", this, &VRManagerChild::FireDOMVRDisplayDisconnectEventInternal, aDisplayID)); } void VRManagerChild::FireDOMVRDisplayPresentChangeEvent(uint32_t aDisplayID) { nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>( "gfx::VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal", this, &VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal, aDisplayID)); } void VRManagerChild::FireDOMVRDisplayMountedEventInternal(uint32_t aDisplayID) { // Iterate over a copy of mListeners, as dispatched events may modify it. nsTArray<RefPtr<dom::VREventObserver>> listeners; listeners = mListeners; for (auto& listener : listeners) { listener->NotifyVRDisplayMounted(aDisplayID); } } void VRManagerChild::FireDOMVRDisplayUnmountedEventInternal(uint32_t aDisplayID) { // Iterate over a copy of mListeners, as dispatched events may modify it. nsTArray<RefPtr<dom::VREventObserver>> listeners; listeners = mListeners; for (auto& listener : listeners) { listener->NotifyVRDisplayUnmounted(aDisplayID); } } void VRManagerChild::FireDOMVRDisplayConnectEventInternal(uint32_t aDisplayID) { // Iterate over a copy of mListeners, as dispatched events may modify it. nsTArray<RefPtr<dom::VREventObserver>> listeners; listeners = mListeners; for (auto& listener : listeners) { listener->NotifyVRDisplayConnect(aDisplayID); } } void VRManagerChild::FireDOMVRDisplayDisconnectEventInternal(uint32_t aDisplayID) { // Iterate over a copy of mListeners, as dispatched events may modify it. nsTArray<RefPtr<dom::VREventObserver>> listeners; listeners = mListeners; for (auto& listener : listeners) { listener->NotifyVRDisplayDisconnect(aDisplayID); } } void VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal(uint32_t aDisplayID) { // Iterate over a copy of mListeners, as dispatched events may modify it. nsTArray<RefPtr<dom::VREventObserver>> listeners; listeners = mListeners; for (auto& listener : listeners) { listener->NotifyVRDisplayPresentChange(aDisplayID); } } void VRManagerChild::AddListener(dom::VREventObserver* aObserver) { MOZ_ASSERT(aObserver); if (mListeners.IndexOf(aObserver) != kNoIndex) { return; // already exists } mListeners.AppendElement(aObserver); if (mListeners.Length() == 1) { Unused << SendSetHaveEventListener(true); } } void VRManagerChild::RemoveListener(dom::VREventObserver* aObserver) { MOZ_ASSERT(aObserver); mListeners.RemoveElement(aObserver); if (mListeners.IsEmpty()) { Unused << SendSetHaveEventListener(false); } } void VRManagerChild::HandleFatalError(const char* aName, const char* aMsg) const { dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid()); } void VRManagerChild::AddPromise(const uint32_t& aID, dom::Promise* aPromise) { MOZ_ASSERT(!mGamepadPromiseList.Get(aID, nullptr)); mGamepadPromiseList.Put(aID, aPromise); } mozilla::ipc::IPCResult VRManagerChild::RecvReplyGamepadVibrateHaptic(const uint32_t& aPromiseID) { // VRManagerChild could be at other processes, but GamepadManager // only exists at the content process or the same process // in non-e10s mode. MOZ_ASSERT(XRE_IsContentProcess() || IsSameProcess()); RefPtr<dom::Promise> p; if (!mGamepadPromiseList.Get(aPromiseID, getter_AddRefs(p))) { MOZ_CRASH("We should always have a promise."); } p->MaybeResolve(true); mGamepadPromiseList.Remove(aPromiseID); return IPC_OK(); } mozilla::ipc::IPCResult VRManagerChild::RecvDispatchSubmitFrameResult(const uint32_t& aDisplayID, const VRSubmitFrameResultInfo& aResult) { for (auto& display : mDisplays) { if (display->GetDisplayInfo().GetDisplayID() == aDisplayID) { display->UpdateSubmitFrameResult(aResult); } } return IPC_OK(); } } // namespace gfx
namespace layers { static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder; static bool sFinishedCompositorShutDown = false; // See ImageBridgeChild.cpp void ReleaseImageBridgeParentSingleton(); CompositorThreadHolder* GetCompositorThreadHolder() { return sCompositorThreadHolder; } base::Thread* CompositorThread() { return sCompositorThreadHolder ? sCompositorThreadHolder->GetCompositorThread() : nullptr; } /* static */ MessageLoop* CompositorThreadHolder::Loop() { return CompositorThread() ? CompositorThread()->message_loop() : nullptr; } CompositorThreadHolder* CompositorThreadHolder::GetSingleton() { return sCompositorThreadHolder; } CompositorThreadHolder::CompositorThreadHolder() : mCompositorThread(CreateCompositorThread()) { MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_CTOR(CompositorThreadHolder); } CompositorThreadHolder::~CompositorThreadHolder() { MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_DTOR(CompositorThreadHolder); DestroyCompositorThread(mCompositorThread); } /* static */ void CompositorThreadHolder::DestroyCompositorThread(base::Thread* aCompositorThread) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet."); CompositorBridgeParent::Shutdown(); delete aCompositorThread; sFinishedCompositorShutDown = true; } /* static */ base::Thread* CompositorThreadHolder::CreateCompositorThread() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); base::Thread* compositorThread = new base::Thread("Compositor"); base::Thread::Options options; /* Timeout values are powers-of-two to enable us get better data. 128ms is chosen for transient hangs because 8Hz should be the minimally acceptable goal for Compositor responsiveness (normal goal is 60Hz). */ options.transient_hang_timeout = 128; // milliseconds /* 2048ms is chosen for permanent hangs because it's longer than most * Compositor hangs seen in the wild, but is short enough to not miss getting * native hang stacks. */ options.permanent_hang_timeout = 2048; // milliseconds #if defined(_WIN32) /* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As * such the thread is a gui thread, and must process a windows message queue or * risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */ options.message_loop_type = MessageLoop::TYPE_UI; #endif if (!compositorThread->StartWithOptions(options)) { delete compositorThread; return nullptr; } CompositorBridgeParent::Setup(); return compositorThread; } void CompositorThreadHolder::Start() { MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); sCompositorThreadHolder = new CompositorThreadHolder(); } void CompositorThreadHolder::Shutdown() { MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!"); ReleaseImageBridgeParentSingleton(); gfx::ReleaseVRManagerParentSingleton(); MediaSystemResourceService::Shutdown(); sCompositorThreadHolder = nullptr; // No locking is needed around sFinishedCompositorShutDown because it is only // ever accessed on the main thread. while (!sFinishedCompositorShutDown) { NS_ProcessNextEvent(nullptr, true); } CompositorBridgeParent::FinishShutdown(); } /* static */ bool CompositorThreadHolder::IsInCompositorThread() { return CompositorThread() && CompositorThread()->thread_id() == PlatformThread::CurrentId(); } } // namespace mozilla