Tickler.cpp (6362B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "Tickler.h" 7 8 #ifdef MOZ_USE_WIFI_TICKLER 9 # include "nsComponentManagerUtils.h" 10 # include "nsINamed.h" 11 # include "nsIPrefService.h" 12 # include "nsServiceManagerUtils.h" 13 # include "nsThreadUtils.h" 14 # include "prnetdb.h" 15 # include "nsXULAppAPI.h" 16 # include "nsIPrefService.h" 17 # include "nsIPrefBranch.h" 18 19 # include "mozilla/java/GeckoAppShellWrappers.h" 20 # include "mozilla/jni/Utils.h" 21 # include "nsXULAppAPI.h" 22 23 namespace mozilla { 24 namespace net { 25 26 NS_IMPL_ISUPPORTS(Tickler, nsISupportsWeakReference, Tickler) 27 28 Tickler::Tickler() 29 : mLock("Tickler::mLock"), 30 mActive(false), 31 mCanceled(false), 32 mEnabled(false), 33 mDelay(16), 34 mDuration(TimeDuration::FromMilliseconds(400)), 35 mFD(nullptr) { 36 MOZ_ASSERT(NS_IsMainThread()); 37 } 38 39 Tickler::~Tickler() { 40 // non main thread uses of the tickler should hold weak 41 // references to it if they must hold a reference at all 42 MOZ_ASSERT(NS_IsMainThread()); 43 44 if (mThread) { 45 mThread->AsyncShutdown(); 46 mThread = nullptr; 47 } 48 49 if (mTimer) mTimer->Cancel(); 50 if (mFD) PR_Close(mFD); 51 } 52 53 nsresult Tickler::Init() { 54 if (!XRE_IsParentProcess()) { 55 return NS_ERROR_FAILURE; 56 } 57 58 MOZ_ASSERT(NS_IsMainThread()); 59 MOZ_ASSERT(!mTimer); 60 MOZ_ASSERT(!mActive); 61 MOZ_ASSERT(!mThread); 62 MOZ_ASSERT(!mFD); 63 64 if (jni::IsAvailable()) { 65 java::GeckoAppShell::EnableNetworkNotifications(); 66 } 67 68 mFD = PR_OpenUDPSocket(PR_AF_INET); 69 if (!mFD) return NS_ERROR_FAILURE; 70 71 // make sure new socket has a ttl of 1 72 // failure is not fatal. 73 PRSocketOptionData opt; 74 opt.option = PR_SockOpt_IpTimeToLive; 75 opt.value.ip_ttl = 1; 76 PR_SetSocketOption(mFD, &opt); 77 78 nsresult rv = NS_NewNamedThread("wifi tickler", getter_AddRefs(mThread)); 79 if (NS_FAILED(rv)) return rv; 80 81 nsCOMPtr<nsITimer> tmpTimer = NS_NewTimer(mThread); 82 if (!tmpTimer) return NS_ERROR_OUT_OF_MEMORY; 83 84 mTimer.swap(tmpTimer); 85 86 mAddr.inet.family = PR_AF_INET; 87 mAddr.inet.port = PR_htons(4886); 88 mAddr.inet.ip = 0; 89 90 return NS_OK; 91 } 92 93 void Tickler::Tickle() { 94 MutexAutoLock lock(mLock); 95 MOZ_ASSERT(mThread); 96 mLastTickle = TimeStamp::Now(); 97 if (!mActive) MaybeStartTickler(); 98 } 99 100 void Tickler::PostCheckTickler() { 101 mLock.AssertCurrentThreadOwns(); 102 mThread->Dispatch(NewRunnableMethod("net::Tickler::CheckTickler", this, 103 &Tickler::CheckTickler), 104 NS_DISPATCH_NORMAL); 105 return; 106 } 107 108 void Tickler::MaybeStartTicklerUnlocked() { 109 MutexAutoLock lock(mLock); 110 MaybeStartTickler(); 111 } 112 113 void Tickler::MaybeStartTickler() { 114 mLock.AssertCurrentThreadOwns(); 115 if (!NS_IsMainThread()) { 116 NS_DispatchToMainThread( 117 NewRunnableMethod("net::Tickler::MaybeStartTicklerUnlocked", this, 118 &Tickler::MaybeStartTicklerUnlocked)); 119 return; 120 } 121 122 if (!mPrefs) mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID); 123 if (mPrefs) { 124 int32_t val; 125 bool boolVal; 126 127 if (NS_SUCCEEDED( 128 mPrefs->GetBoolPref("network.tickle-wifi.enabled", &boolVal))) 129 mEnabled = boolVal; 130 131 if (NS_SUCCEEDED( 132 mPrefs->GetIntPref("network.tickle-wifi.duration", &val))) { 133 if (val < 1) val = 1; 134 if (val > 100000) val = 100000; 135 mDuration = TimeDuration::FromMilliseconds(val); 136 } 137 138 if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.delay", &val))) { 139 if (val < 1) val = 1; 140 if (val > 1000) val = 1000; 141 mDelay = static_cast<uint32_t>(val); 142 } 143 } 144 145 PostCheckTickler(); 146 } 147 148 void Tickler::CheckTickler() { 149 MutexAutoLock lock(mLock); 150 MOZ_ASSERT(mThread == NS_GetCurrentThread()); 151 152 bool shouldRun = 153 (!mCanceled) && ((TimeStamp::Now() - mLastTickle) <= mDuration); 154 155 if ((shouldRun && mActive) || (!shouldRun && !mActive)) 156 return; // no change in state 157 158 if (mActive) 159 StopTickler(); 160 else 161 StartTickler(); 162 } 163 164 void Tickler::Cancel() { 165 MutexAutoLock lock(mLock); 166 MOZ_ASSERT(NS_IsMainThread()); 167 mCanceled = true; 168 if (mThread) PostCheckTickler(); 169 } 170 171 void Tickler::StopTickler() { 172 mLock.AssertCurrentThreadOwns(); 173 MOZ_ASSERT(mThread == NS_GetCurrentThread()); 174 MOZ_ASSERT(mTimer); 175 MOZ_ASSERT(mActive); 176 177 mTimer->Cancel(); 178 mActive = false; 179 } 180 181 class TicklerTimer final : public nsITimerCallback, public nsINamed { 182 NS_DECL_THREADSAFE_ISUPPORTS 183 NS_DECL_NSITIMERCALLBACK 184 185 explicit TicklerTimer(Tickler* aTickler) { 186 mTickler = do_GetWeakReference(aTickler); 187 } 188 189 // nsINamed 190 NS_IMETHOD GetName(nsACString& aName) override { 191 aName.AssignLiteral("TicklerTimer"); 192 return NS_OK; 193 } 194 195 private: 196 ~TicklerTimer() {} 197 198 nsWeakPtr mTickler; 199 }; 200 201 void Tickler::StartTickler() { 202 mLock.AssertCurrentThreadOwns(); 203 MOZ_ASSERT(mThread == NS_GetCurrentThread()); 204 MOZ_ASSERT(!mActive); 205 MOZ_ASSERT(mTimer); 206 207 auto tickler = MakeRefPtr<TicklerTimer>(this); 208 if (NS_SUCCEEDED(mTimer->InitWithCallback(tickler, mEnabled ? mDelay : 1000, 209 nsITimer::TYPE_REPEATING_SLACK))) 210 mActive = true; 211 } 212 213 // argument should be in network byte order 214 void Tickler::SetIPV4Address(uint32_t address) { mAddr.inet.ip = address; } 215 216 // argument should be in network byte order 217 void Tickler::SetIPV4Port(uint16_t port) { mAddr.inet.port = port; } 218 219 NS_IMPL_ISUPPORTS(TicklerTimer, nsITimerCallback, nsINamed) 220 221 NS_IMETHODIMP TicklerTimer::Notify(nsITimer* timer) { 222 RefPtr<Tickler> tickler = do_QueryReferent(mTickler); 223 if (!tickler) return NS_ERROR_FAILURE; 224 MutexAutoLock lock(tickler->mLock); 225 226 if (!tickler->mFD) { 227 tickler->StopTickler(); 228 return NS_ERROR_FAILURE; 229 } 230 231 if (tickler->mCanceled || 232 ((TimeStamp::Now() - tickler->mLastTickle) > tickler->mDuration)) { 233 tickler->StopTickler(); 234 return NS_OK; 235 } 236 237 if (!tickler->mEnabled) return NS_OK; 238 239 PR_SendTo(tickler->mFD, "", 0, 0, &tickler->mAddr, 0); 240 return NS_OK; 241 } 242 243 } // namespace net 244 } // namespace mozilla 245 246 #else // not defined MOZ_USE_WIFI_TICKLER 247 248 namespace mozilla { 249 namespace net { 250 NS_IMPL_ISUPPORTS0(Tickler) 251 } // namespace net 252 } // namespace mozilla 253 254 #endif // defined MOZ_USE_WIFI_TICKLER