Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
6 #include "Tickler.h"
8 #ifdef MOZ_USE_WIFI_TICKLER
9 #include "nsComponentManagerUtils.h"
10 #include "nsIPrefBranch.h"
11 #include "nsIPrefService.h"
12 #include "nsServiceManagerUtils.h"
13 #include "nsThreadUtils.h"
14 #include "prnetdb.h"
16 #include "AndroidBridge.h"
18 namespace mozilla {
19 namespace net {
21 NS_IMPL_ISUPPORTS(Tickler, nsISupportsWeakReference, Tickler)
23 Tickler::Tickler()
24 : mLock("Tickler::mLock")
25 , mActive(false)
26 , mCanceled(false)
27 , mEnabled(false)
28 , mDelay(16)
29 , mDuration(TimeDuration::FromMilliseconds(400))
30 , mFD(nullptr)
31 {
32 MOZ_ASSERT(NS_IsMainThread());
33 }
35 class TicklerThreadDestructor : public nsRunnable
36 {
37 public:
38 explicit TicklerThreadDestructor(nsIThread *aThread)
39 : mThread(aThread) { }
41 NS_IMETHOD Run() MOZ_OVERRIDE
42 {
43 MOZ_ASSERT(NS_IsMainThread());
44 if (mThread)
45 mThread->Shutdown();
46 return NS_OK;
47 }
49 private:
50 ~TicklerThreadDestructor() { }
51 nsCOMPtr<nsIThread> mThread;
52 };
54 Tickler::~Tickler()
55 {
56 // non main thread uses of the tickler should hold weak
57 // references to it if they must hold a reference at all
58 MOZ_ASSERT(NS_IsMainThread());
60 // Shutting down a thread can spin the event loop - which is a surprising
61 // thing to do from a dtor. Running it on its own event is safer.
62 nsRefPtr<nsIRunnable> event = new TicklerThreadDestructor(mThread);
63 if (NS_FAILED(NS_DispatchToCurrentThread(event))) {
64 mThread->Shutdown();
65 }
66 mThread = nullptr;
68 if (mTimer)
69 mTimer->Cancel();
70 if (mFD)
71 PR_Close(mFD);
72 }
74 nsresult
75 Tickler::Init()
76 {
77 MOZ_ASSERT(NS_IsMainThread());
78 MOZ_ASSERT(!mTimer);
79 MOZ_ASSERT(!mActive);
80 MOZ_ASSERT(!mThread);
81 MOZ_ASSERT(!mFD);
83 if (AndroidBridge::HasEnv()) {
84 mozilla::widget::android::GeckoAppShell::EnableNetworkNotifications();
85 }
87 mFD = PR_OpenUDPSocket(PR_AF_INET);
88 if (!mFD)
89 return NS_ERROR_FAILURE;
91 // make sure new socket has a ttl of 1
92 // failure is not fatal.
93 PRSocketOptionData opt;
94 opt.option = PR_SockOpt_IpTimeToLive;
95 opt.value.ip_ttl = 1;
96 PR_SetSocketOption(mFD, &opt);
98 nsresult rv = NS_NewNamedThread("wifi tickler",
99 getter_AddRefs(mThread));
100 if (NS_FAILED(rv))
101 return rv;
103 nsCOMPtr<nsITimer> tmpTimer(do_CreateInstance(NS_TIMER_CONTRACTID, &rv));
104 if (NS_FAILED(rv))
105 return rv;
107 rv = tmpTimer->SetTarget(mThread);
108 if (NS_FAILED(rv))
109 return rv;
111 mTimer.swap(tmpTimer);
113 mAddr.inet.family = PR_AF_INET;
114 mAddr.inet.port = PR_htons (4886);
115 mAddr.inet.ip = 0;
117 return NS_OK;
118 }
120 void Tickler::Tickle()
121 {
122 MutexAutoLock lock(mLock);
123 MOZ_ASSERT(mThread);
124 mLastTickle = TimeStamp::Now();
125 if (!mActive)
126 MaybeStartTickler();
127 }
129 void Tickler::PostCheckTickler()
130 {
131 mLock.AssertCurrentThreadOwns();
132 mThread->Dispatch(NS_NewRunnableMethod(this, &Tickler::CheckTickler),
133 NS_DISPATCH_NORMAL);
134 return;
135 }
137 void Tickler::MaybeStartTicklerUnlocked()
138 {
139 MutexAutoLock lock(mLock);
140 MaybeStartTickler();
141 }
143 void Tickler::MaybeStartTickler()
144 {
145 mLock.AssertCurrentThreadOwns();
146 if (!NS_IsMainThread()) {
147 NS_DispatchToMainThread(
148 NS_NewRunnableMethod(this, &Tickler::MaybeStartTicklerUnlocked),
149 NS_DISPATCH_NORMAL);
150 return;
151 }
153 if (!mPrefs)
154 mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
155 if (mPrefs) {
156 int32_t val;
157 bool boolVal;
159 if (NS_SUCCEEDED(mPrefs->GetBoolPref("network.tickle-wifi.enabled", &boolVal)))
160 mEnabled = boolVal;
162 if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.duration", &val))) {
163 if (val < 1)
164 val = 1;
165 if (val > 100000)
166 val = 100000;
167 mDuration = TimeDuration::FromMilliseconds(val);
168 }
170 if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.delay", &val))) {
171 if (val < 1)
172 val = 1;
173 if (val > 1000)
174 val = 1000;
175 mDelay = static_cast<uint32_t>(val);
176 }
177 }
179 PostCheckTickler();
180 }
182 void Tickler::CheckTickler()
183 {
184 MutexAutoLock lock(mLock);
185 MOZ_ASSERT(mThread == NS_GetCurrentThread());
187 bool shouldRun = (!mCanceled) &&
188 ((TimeStamp::Now() - mLastTickle) <= mDuration);
190 if ((shouldRun && mActive) || (!shouldRun && !mActive))
191 return; // no change in state
193 if (mActive)
194 StopTickler();
195 else
196 StartTickler();
197 }
199 void Tickler::Cancel()
200 {
201 MutexAutoLock lock(mLock);
202 MOZ_ASSERT(NS_IsMainThread());
203 mCanceled = true;
204 if (mThread)
205 PostCheckTickler();
206 }
208 void Tickler::StopTickler()
209 {
210 mLock.AssertCurrentThreadOwns();
211 MOZ_ASSERT(mThread == NS_GetCurrentThread());
212 MOZ_ASSERT(mTimer);
213 MOZ_ASSERT(mActive);
215 mTimer->Cancel();
216 mActive = false;
217 }
219 class TicklerTimer MOZ_FINAL : public nsITimerCallback
220 {
221 NS_DECL_THREADSAFE_ISUPPORTS
222 NS_DECL_NSITIMERCALLBACK
224 TicklerTimer(Tickler *aTickler)
225 {
226 mTickler = do_GetWeakReference(aTickler);
227 }
229 ~TicklerTimer() {};
231 private:
232 nsWeakPtr mTickler;
233 };
235 void Tickler::StartTickler()
236 {
237 mLock.AssertCurrentThreadOwns();
238 MOZ_ASSERT(mThread == NS_GetCurrentThread());
239 MOZ_ASSERT(!mActive);
240 MOZ_ASSERT(mTimer);
242 if (NS_SUCCEEDED(mTimer->InitWithCallback(new TicklerTimer(this),
243 mEnabled ? mDelay : 1000,
244 nsITimer::TYPE_REPEATING_SLACK)))
245 mActive = true;
246 }
248 // argument should be in network byte order
249 void Tickler::SetIPV4Address(uint32_t address)
250 {
251 mAddr.inet.ip = address;
252 }
254 // argument should be in network byte order
255 void Tickler::SetIPV4Port(uint16_t port)
256 {
257 mAddr.inet.port = port;
258 }
260 NS_IMPL_ISUPPORTS(TicklerTimer, nsITimerCallback)
262 NS_IMETHODIMP TicklerTimer::Notify(nsITimer *timer)
263 {
264 nsRefPtr<Tickler> tickler = do_QueryReferent(mTickler);
265 if (!tickler)
266 return NS_ERROR_FAILURE;
267 MutexAutoLock lock(tickler->mLock);
269 if (!tickler->mFD) {
270 tickler->StopTickler();
271 return NS_ERROR_FAILURE;
272 }
274 if (tickler->mCanceled ||
275 ((TimeStamp::Now() - tickler->mLastTickle) > tickler->mDuration)) {
276 tickler->StopTickler();
277 return NS_OK;
278 }
280 if (!tickler->mEnabled)
281 return NS_OK;
283 PR_SendTo(tickler->mFD, "", 0, 0, &tickler->mAddr, 0);
284 return NS_OK;
285 }
287 } // namespace mozilla::net
288 } // namespace mozilla
290 #else // not defined MOZ_USE_WIFI_TICKLER
292 namespace mozilla {
293 namespace net {
294 NS_IMPL_ISUPPORTS0(Tickler)
295 } // namespace mozilla::net
296 } // namespace mozilla
298 #endif // defined MOZ_USE_WIFI_TICKLER