diff options
Diffstat (limited to 'netwerk/base/Tickler.cpp')
-rw-r--r-- | netwerk/base/Tickler.cpp | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/netwerk/base/Tickler.cpp b/netwerk/base/Tickler.cpp new file mode 100644 index 000000000..555fdbbe5 --- /dev/null +++ b/netwerk/base/Tickler.cpp @@ -0,0 +1,277 @@ +/* -*- 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 "Tickler.h" + +#ifdef MOZ_USE_WIFI_TICKLER +#include "nsComponentManagerUtils.h" +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "prnetdb.h" + +#include "mozilla/jni/Utils.h" +#include "GeneratedJNIWrappers.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(Tickler, nsISupportsWeakReference, Tickler) + +Tickler::Tickler() + : mLock("Tickler::mLock") + , mActive(false) + , mCanceled(false) + , mEnabled(false) + , mDelay(16) + , mDuration(TimeDuration::FromMilliseconds(400)) + , mFD(nullptr) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +Tickler::~Tickler() +{ + // non main thread uses of the tickler should hold weak + // references to it if they must hold a reference at all + MOZ_ASSERT(NS_IsMainThread()); + + if (mThread) { + mThread->AsyncShutdown(); + mThread = nullptr; + } + + if (mTimer) + mTimer->Cancel(); + if (mFD) + PR_Close(mFD); +} + +nsresult +Tickler::Init() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mTimer); + MOZ_ASSERT(!mActive); + MOZ_ASSERT(!mThread); + MOZ_ASSERT(!mFD); + + if (jni::IsAvailable()) { + java::GeckoAppShell::EnableNetworkNotifications(); + } + + mFD = PR_OpenUDPSocket(PR_AF_INET); + if (!mFD) + return NS_ERROR_FAILURE; + + // make sure new socket has a ttl of 1 + // failure is not fatal. + PRSocketOptionData opt; + opt.option = PR_SockOpt_IpTimeToLive; + opt.value.ip_ttl = 1; + PR_SetSocketOption(mFD, &opt); + + nsresult rv = NS_NewNamedThread("wifi tickler", + getter_AddRefs(mThread)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsITimer> tmpTimer(do_CreateInstance(NS_TIMER_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return rv; + + rv = tmpTimer->SetTarget(mThread); + if (NS_FAILED(rv)) + return rv; + + mTimer.swap(tmpTimer); + + mAddr.inet.family = PR_AF_INET; + mAddr.inet.port = PR_htons (4886); + mAddr.inet.ip = 0; + + return NS_OK; +} + +void Tickler::Tickle() +{ + MutexAutoLock lock(mLock); + MOZ_ASSERT(mThread); + mLastTickle = TimeStamp::Now(); + if (!mActive) + MaybeStartTickler(); +} + +void Tickler::PostCheckTickler() +{ + mLock.AssertCurrentThreadOwns(); + mThread->Dispatch(NewRunnableMethod(this, &Tickler::CheckTickler), + NS_DISPATCH_NORMAL); + return; +} + +void Tickler::MaybeStartTicklerUnlocked() +{ + MutexAutoLock lock(mLock); + MaybeStartTickler(); +} + +void Tickler::MaybeStartTickler() +{ + mLock.AssertCurrentThreadOwns(); + if (!NS_IsMainThread()) { + NS_DispatchToMainThread( + NewRunnableMethod(this, &Tickler::MaybeStartTicklerUnlocked)); + return; + } + + if (!mPrefs) + mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (mPrefs) { + int32_t val; + bool boolVal; + + if (NS_SUCCEEDED(mPrefs->GetBoolPref("network.tickle-wifi.enabled", &boolVal))) + mEnabled = boolVal; + + if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.duration", &val))) { + if (val < 1) + val = 1; + if (val > 100000) + val = 100000; + mDuration = TimeDuration::FromMilliseconds(val); + } + + if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.delay", &val))) { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + mDelay = static_cast<uint32_t>(val); + } + } + + PostCheckTickler(); +} + +void Tickler::CheckTickler() +{ + MutexAutoLock lock(mLock); + MOZ_ASSERT(mThread == NS_GetCurrentThread()); + + bool shouldRun = (!mCanceled) && + ((TimeStamp::Now() - mLastTickle) <= mDuration); + + if ((shouldRun && mActive) || (!shouldRun && !mActive)) + return; // no change in state + + if (mActive) + StopTickler(); + else + StartTickler(); +} + +void Tickler::Cancel() +{ + MutexAutoLock lock(mLock); + MOZ_ASSERT(NS_IsMainThread()); + mCanceled = true; + if (mThread) + PostCheckTickler(); +} + +void Tickler::StopTickler() +{ + mLock.AssertCurrentThreadOwns(); + MOZ_ASSERT(mThread == NS_GetCurrentThread()); + MOZ_ASSERT(mTimer); + MOZ_ASSERT(mActive); + + mTimer->Cancel(); + mActive = false; +} + +class TicklerTimer final : public nsITimerCallback +{ + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITIMERCALLBACK + + TicklerTimer(Tickler *aTickler) + { + mTickler = do_GetWeakReference(aTickler); + } + +private: + ~TicklerTimer() {} + + nsWeakPtr mTickler; +}; + +void Tickler::StartTickler() +{ + mLock.AssertCurrentThreadOwns(); + MOZ_ASSERT(mThread == NS_GetCurrentThread()); + MOZ_ASSERT(!mActive); + MOZ_ASSERT(mTimer); + + if (NS_SUCCEEDED(mTimer->InitWithCallback(new TicklerTimer(this), + mEnabled ? mDelay : 1000, + nsITimer::TYPE_REPEATING_SLACK))) + mActive = true; +} + +// argument should be in network byte order +void Tickler::SetIPV4Address(uint32_t address) +{ + mAddr.inet.ip = address; +} + +// argument should be in network byte order +void Tickler::SetIPV4Port(uint16_t port) +{ + mAddr.inet.port = port; +} + +NS_IMPL_ISUPPORTS(TicklerTimer, nsITimerCallback) + +NS_IMETHODIMP TicklerTimer::Notify(nsITimer *timer) +{ + RefPtr<Tickler> tickler = do_QueryReferent(mTickler); + if (!tickler) + return NS_ERROR_FAILURE; + MutexAutoLock lock(tickler->mLock); + + if (!tickler->mFD) { + tickler->StopTickler(); + return NS_ERROR_FAILURE; + } + + if (tickler->mCanceled || + ((TimeStamp::Now() - tickler->mLastTickle) > tickler->mDuration)) { + tickler->StopTickler(); + return NS_OK; + } + + if (!tickler->mEnabled) + return NS_OK; + + PR_SendTo(tickler->mFD, "", 0, 0, &tickler->mAddr, 0); + return NS_OK; +} + +} // namespace mozilla::net +} // namespace mozilla + +#else // not defined MOZ_USE_WIFI_TICKLER + +namespace mozilla { +namespace net { +NS_IMPL_ISUPPORTS0(Tickler) +} // namespace net +} // namespace mozilla + +#endif // defined MOZ_USE_WIFI_TICKLER + |